Skip to main content

SessionDraft

Struct SessionDraft 

Source
struct SessionDraft {
    initial: DraftVersion,
    settled: Sender<Option<(DraftVersion, DraftSource)>>,
    deadline: Instant,
    control_stream_open: AtomicBool,
}
Expand description

The draft this session frames with, and the one place every task reads it from.

§Why a shared cell rather than a field

Drafts 07 to 14 all negotiate the same ALPN, so a session in that cohort starts on the draft its configuration named and learns the wire’s answer from the first SETUP on the control stream. Everything that has to agree with that answer — the object framer on every data stream, the datagram header decoder, the control-frame walker that places an injection, and the capability table each hook site is shown — lives in a task that was spawned before the control stream was even accepted. A draft copied into each of those tasks is a copy of the guess, and no later correction can reach it.

§Reading it

Self::now is the non-blocking read: the best answer so far, or the starting draft while there is none. Self::resolved is the ordering edge — it waits for an answer, and is what a task calls when running on the wrong draft would produce a wrong result rather than a stale label.

§Writing it

Self::settle takes the first write of each rank and keeps the highest (see DraftSource). Both control directions write: the client’s direction from CLIENT_SETUP and the relay’s from SERVER_SETUP, so the pair converges on the version the peers agreed rather than on whichever direction was read first.

Fields§

§initial: DraftVersion

The draft chosen before the relay was dialled — the ALPN’s answer where there is one, and the configured draft otherwise. What Self::now answers while nothing has settled, and what the deadline settles on.

§settled: Sender<Option<(DraftVersion, DraftSource)>>

The best answer so far, or None while the session is still running on initial. A watch rather than an atomic because the waiters are the point: this is what Self::resolved parks on.

§deadline: Instant

The instant Self::resolved stops waiting. Absolute, and shared by every waiter, so a session pays this window once rather than once per stream: the first waiter to reach it settles the cell, and every waiter after that returns immediately.

§control_stream_open: AtomicBool

Whether this session has a control stream at all yet.

The only thing that can name a draft is a SETUP, and the only place a SETUP arrives is a control stream. Until one exists there is nothing to wait for, so Self::resolved does not wait — which is what keeps the window off the timing of a session that never opens one.

It is a latch and not a promise. A peer that opened a data stream before its control stream gets the starting draft on that one stream, which is the same answer it would have got with no cell at all; every draft in the cohort puts the setup exchange first, so a session in which that happens is not one they describe.

Implementations§

Source§

impl SessionDraft

Source

fn new(initial: DraftVersion, fixed: bool) -> Self

The cell for a session starting on initial.

fixed is whether that draft came from the ALPN. A fixed session is born settled, so it never waits and no SETUP peek can move it — which is the right reading of drafts 15 and later, where the SETUP message carries no version at all.

Source

fn note_control_stream(&self)

Record that this session now has a control stream.

Called where one starts being forwarded, in both topologies. What it buys is the absence of a wait everywhere else: see Self::control_stream_open.

Source

fn now(&self) -> DraftVersion

The best answer so far, without waiting for a better one.

Source

fn settle(&self, draft: DraftVersion, source: DraftSource) -> bool

Record draft as this session’s, if source outranks what is held.

Answers whether it landed, so a caller that has work to do only when the session’s draft actually moved can ask rather than compare.

Source

async fn resolved(&self, cancel: &CancellationToken) -> DraftVersion

The draft, waited for.

Returns at once when the session already has an answer, which is every session whose ALPN named a draft and every session whose control stream has already been read. It also returns at once when the session has no control stream yet, because nothing else can answer and waiting would put DRAFT_SETTLE_WINDOW on the front of every stream of a session that never opens one.

Otherwise it waits for one of three things: a SETUP naming the draft, DRAFT_SETTLE_WINDOW expiring, or the session being cancelled — the last of which is why a teardown is not held up by a window that has barely started.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

§

impl<T> Instrument for T

§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided [Span], returning an Instrumented wrapper. Read more
§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
§

impl<T> WithSubscriber for T

§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a [WithDispatch] wrapper. Read more
§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a [WithDispatch] wrapper. Read more