use bytes::Buf; use futures_util::task::AtomicWaker; use std::io; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::Arc; use std::task::{Context, Poll}; use tokio::io::AsyncWrite;
/// Acknowledges ping requests from the remote. #[derive(Debug)] pub(crate) struct PingPong {
pending_ping: Option<PendingPing>,
pending_pong: Option<PingPayload>,
user_pings: Option<UserPingsRx>,
}
#[derive(Debug)] struct UserPingsInner {
state: AtomicUsize, /// Task to wake up the main `Connection`.
ping_task: AtomicWaker, /// Task to wake up `share::PingPong::poll_pong`.
pong_task: AtomicWaker,
}
/// Status returned from `PingPong::recv_ping`. #[derive(Debug)] pub(crate) enum ReceivedPing {
MustAck,
Unknown,
Shutdown,
}
/// No user ping pending. const USER_STATE_EMPTY: usize = 0; /// User has called `send_ping`, but PING hasn't been written yet. const USER_STATE_PENDING_PING: usize = 1; /// User PING has been written, waiting for PONG. const USER_STATE_PENDING_PONG: usize = 2; /// We've received user PONG, waiting for user to `poll_pong`. const USER_STATE_RECEIVED_PONG: usize = 3; /// The connection is closed. const USER_STATE_CLOSED: usize = 4;
/// Can only be called once. If called a second time, returns `None`. pub(crate) fn take_user_pings(&mutself) -> Option<UserPings> { ifself.user_pings.is_some() { return None;
}
/// Process a ping pub(crate) fn recv_ping(&mutself, ping: Ping) -> ReceivedPing { // The caller should always check that `send_pongs` returns ready before // calling `recv_ping`.
assert!(self.pending_pong.is_none());
if ping.is_ack() { iflet Some(pending) = self.pending_ping.take() { if &pending.payload == ping.payload() {
assert_eq!(
&pending.payload,
&Ping::SHUTDOWN, "pending_ping should be for shutdown",
);
tracing::trace!("recv PING SHUTDOWN ack"); return ReceivedPing::Shutdown;
}
// if not the payload we expected, put it back. self.pending_ping = Some(pending);
}
// else we were acked a ping we didn't send? // The spec doesn't require us to do anything about this, // so for resiliency, just ignore it for now.
tracing::warn!("recv PING ack that we never sent: {:?}", ping);
ReceivedPing::Unknown
} else { // Save the ping's payload to be sent as an acknowledgement. self.pending_pong = Some(ping.into_payload());
ReceivedPing::MustAck
}
}
impl UserPings { pub(crate) fn send_ping(&self) -> Result<(), Option<proto::Error>> { let prev = self
.0
.state
.compare_exchange(
USER_STATE_EMPTY, // current
USER_STATE_PENDING_PING, // new
Ordering::AcqRel,
Ordering::Acquire,
)
.unwrap_or_else(|v| v);
match prev {
USER_STATE_EMPTY => { self.0.ping_task.wake();
Ok(())
}
USER_STATE_CLOSED => Err(Some(broken_pipe().into())),
_ => { // Was already pending, user error!
Err(None)
}
}
}
pub(crate) fn poll_pong(&self, cx: &mut Context) -> Poll<Result<(), proto::Error>> { // Must register before checking state, in case state were to change // before we could register, and then the ping would just be lost. self.0.pong_task.register(cx.waker()); let prev = self
.0
.state
.compare_exchange(
USER_STATE_RECEIVED_PONG, // current
USER_STATE_EMPTY, // new
Ordering::AcqRel,
Ordering::Acquire,
)
.unwrap_or_else(|v| v);
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.18Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-20)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.