struct StopWatcher {
watching: Option<Pin<Box<dyn Future<Output = Result<(), TransportError>> + Send>>>,
armed: bool,
}Expand description
A destination-side STOP_SENDING watcher, hoisted once per forwarded
stream and used as a fourth tokio::select! branch.
§Why it is hoisted
tokio::select! drops and rebuilds every branch future each time round
the loop. Rebuilding SendStream::stopped() takes quinn’s connection
state lock and inserts into a per-connection map
(quinn-0.11.9/src/send_stream.rs:258-263), which is per-wake work on
loops documented as doing none. So the future is built once, lives here
across iterations, and Self::watch borrows it rather than moving
it — a select! iteration that cancels this branch therefore loses
nothing and resumes the same future next time round.
§Why it is fused
Building it once means it can only resolve once: polling a completed
future panics with “async fn resumed after completion”. Self::watch
clears the slot the instant the future returns, which both disables the
branch (through Self::is_watching) and makes a re-poll structurally
unreachable. The fuse is not belt and braces — without it the very next
select! iteration panics inside the forwarding task.
Self::armed is what keeps the fuse one-way: a retired watcher has an
empty slot, and without the flag the next Self::arm would rebuild it.
§Cost
One Box::pin per forwarded stream, allocated at the first select!
iteration and never again. Per stream, never per object.
Fields§
§watching: Option<Pin<Box<dyn Future<Output = Result<(), TransportError>> + Send>>>The hoisted stopped() future. None before Self::arm, and
again once it has resolved or Self::retire was called.
armed: boolSet by the first Self::arm, so a retired watcher stays retired.
Implementations§
Source§impl StopWatcher
impl StopWatcher
Sourcefn arm(&mut self, send: &SendStream)
fn arm(&mut self, send: &SendStream)
Build the watcher over send, once.
Called from the top of each pipe’s loop rather than before it, so
pipe_data_passthrough — whose contract is a stack buffer and a write
— allocates on its first select! iteration and not at function entry.
Idempotent: a second call is a no-op, and a call after Self::retire
does not re-arm.
Sourcefn is_watching(&self) -> bool
fn is_watching(&self) -> bool
Whether the select! branch should be enabled this iteration.
Sourcefn retire(&mut self)
fn retire(&mut self)
Drop the watcher without polling it again.
Called before every local send.reset: quinn keeps no
stopped-notification for a stream it has locally reset, so a
watcher held across a reset stays pending until the connection ends
(see SendStream::stopped’s own docs). Every reset site returns
from its pipe immediately afterwards, so this is about saying what
the code means as much as about the residue.
Sourceasync fn watch(&mut self) -> Result<(), TransportError>
async fn watch(&mut self) -> Result<(), TransportError>
Resolve when the destination stops being useful — then never again.
Stays pending forever once retired, so an enabled-but-retired
branch cannot spin; the if guard is the fast path and this is the
backstop.
Cancellation-safe: the fuse below is reached only on completion, so
a select! iteration that drops this future mid-poll leaves the
hoisted future exactly where it was.