Skip to main content

dolang_rpc/
trailer.rs

1//! Streaming request or response byte trailers.
2
3use std::{
4    io::{self, IoSlice},
5    marker::PhantomData,
6    mem::MaybeUninit,
7    pin::Pin,
8    sync::{Arc, Mutex, MutexGuard},
9    task::{Context, Poll, Waker},
10};
11
12use bytes::{Buf, BufMut, BytesMut};
13use futures::io::{AsyncRead, AsyncWrite};
14
15use crate::{
16    Limits,
17    fragment::Kind,
18    fragment::{Flags, FragmentHeader},
19    transport::{AnyRecv, AnySend, RecvFrame, SendFrame},
20    window::{ControlSink, SessionWindow},
21};
22
23fn lock<T>(mutex: &Mutex<T>) -> MutexGuard<'_, T> {
24    mutex
25        .lock()
26        .unwrap_or_else(|poisoned| poisoned.into_inner())
27}
28
29fn register_waker(slot: &mut Option<Waker>, waker: &Waker) {
30    if !slot
31        .as_ref()
32        .is_some_and(|current| current.will_wake(waker))
33    {
34        *slot = Some(waker.clone());
35    }
36}
37
38fn wake(waker: Option<Waker>) {
39    if let Some(waker) = waker {
40        waker.wake();
41    }
42}
43
44#[derive(Clone, Copy, Debug, Eq, PartialEq)]
45pub(crate) enum SendAction {
46    Fragment,
47    Finish,
48    Abort,
49}
50
51#[derive(Clone, Copy, Debug, Eq, PartialEq)]
52enum SendState {
53    /// Nothing staged, no fragment in flight, no token requested.
54    Idle,
55    /// The writer wants to send a fragment and has asked to be scheduled,
56    /// but no token has been granted yet.
57    Demand,
58    /// A token was just granted in response to `Demand`. `wait_fragment` is
59    /// giving the writer one grace period to show up and use the zero-copy
60    /// fast path directly against the token.
61    Granted,
62    /// The grace period following `Granted` expired without the writer
63    /// reappearing. The token is still held; the *next* `poll_write` stages
64    /// directly into `buffer` instead of attempting zero-copy, and wakes
65    /// the driver itself — no further grant/round-trip is needed.
66    Staging,
67    /// `buffer` holds a fragment (header + payload, or whatever's left of
68    /// one) ready to flush.
69    Fragment,
70    /// `buffer` still holds an unflushed fragment *and* the writer has
71    /// more data ready to stage as soon as it drains.
72    FragmentDemand,
73    Finish,
74    FragmentFinish,
75    /// A clean, local abort (peer discard, cancellation, or the producer
76    /// dropping `TrailerSend` without finishing): the driver observes this
77    /// as an ordinary `SendAction::Abort` (wire `ABORT` fragment, no
78    /// connection-level failure), while the producer observes it as
79    /// `SendShared::error` on its next `poll_write`/`poll_flush`. `state`
80    /// is authoritative for whether `error` is set — always paired with it,
81    /// never checked independently.
82    Abort,
83    /// A genuine I/O failure (mid-flush or mid zero-copy write). Both the
84    /// producer and the driver observe this as `Err` (via `error`, same
85    /// pairing as `Abort`); for the driver it propagates out of
86    /// `wait_fragment` as connection-fatal.
87    Failed,
88}
89
90pub(crate) struct SendShared {
91    token: Option<AnySend<'static>>,
92    kind: Kind,
93    id: u64,
94    max_fragment_size: usize,
95    copy_threshold: usize,
96    /// Total bytes committed to fragments so far (staged or written
97    /// zero-copy), regardless of whether they've actually reached the wire
98    /// yet.
99    written: usize,
100    /// Pool credit held for the fragment currently being formed but not yet
101    /// committed. Nonzero only while a large write is waiting for its
102    /// transport grant: the reservation is taken in `Idle` and carried across
103    /// `Demand` so that reaching `Granted`/`Staging` always has credit in
104    /// hand, and those states never have to park while holding a lease.
105    reserved: usize,
106    /// The connection-wide credit pool, and the only thing bounding what this
107    /// trailer may have outstanding — there is no per-trailer window, see
108    /// [`SessionWindow`]. Outstanding bytes are tracked there by message id,
109    /// not here, so they can still be settled once this `SendShared` is gone.
110    session: Arc<SessionWindow>,
111    /// Unsent suffix of a committed fragment. While `poll_write` holds the
112    /// mutex, this temporarily contains the header before it is committed.
113    buffer: BytesMut,
114    state: SendState,
115    /// Set exactly when `state` is `Abort` or `Failed`, cleared never
116    /// (states never revert). Only ever read once `state` has already
117    /// established one of those two, so it doesn't need its own
118    /// `is_some()`/`is_none()` check anywhere.
119    error: Option<(io::ErrorKind, String)>,
120    /// Whether the message carrying this trailer has been admitted to the
121    /// scheduler's active set.
122    ///
123    /// A trailer cannot precede its own payload on the wire, so a producer
124    /// writing into a message that has not started yet can make no progress
125    /// whatever it does. Reserving credit anyway is worse than useless: it is
126    /// a deadlock, because payload quota and trailer credit are exactly the
127    /// two things such a message is waiting for. Several unstarted messages
128    /// can between them reserve the whole trailer pool for fragments that
129    /// cannot be sent, starving the started messages whose completion is the
130    /// only thing that would release the payload quota they are waiting on.
131    started: bool,
132    writer_waker: Option<Waker>,
133    driver_waker: Option<Waker>,
134}
135
136impl SendShared {
137    pub(crate) fn new(
138        kind: Kind,
139        id: u64,
140        limits: &Limits,
141        session: Arc<SessionWindow>,
142    ) -> Arc<Mutex<Self>> {
143        Arc::new(Mutex::new(Self {
144            token: None,
145            kind,
146            id,
147            max_fragment_size: limits.max_fragment_size,
148            copy_threshold: limits.trailer_send_copy_threshold,
149            written: 0,
150            reserved: 0,
151            session,
152            buffer: BytesMut::new(),
153            state: SendState::Idle,
154            error: None,
155            started: false,
156            writer_waker: None,
157            driver_waker: None,
158        }))
159    }
160
161    /// Announces that the message carrying this trailer has been admitted and
162    /// will be driven, releasing any producer parked on that.
163    pub(crate) fn start(shared: &Mutex<Self>) {
164        let mut inner = lock(shared);
165        if inner.started {
166            return;
167        }
168        inner.started = true;
169        let writer = inner.writer_waker.take();
170        drop(inner);
171        wake(writer);
172    }
173
174    pub(crate) fn poll_action(shared: &Mutex<Self>, cx: &mut Context<'_>) -> Poll<SendAction> {
175        let mut inner = lock(shared);
176        inner.driver_waker.take();
177        match inner.state {
178            SendState::Demand
179            | SendState::Fragment
180            | SendState::FragmentDemand
181            | SendState::FragmentFinish => Poll::Ready(SendAction::Fragment),
182            SendState::Finish => Poll::Ready(SendAction::Finish),
183            SendState::Abort => Poll::Ready(SendAction::Abort),
184            SendState::Idle | SendState::Granted | SendState::Staging => {
185                register_waker(&mut inner.driver_waker, cx.waker());
186                Poll::Pending
187            }
188            SendState::Failed => {
189                // Defensive: `Failed` is only ever set while a lease from
190                // `grant` is live (mid-flush, or by the producer's own
191                // zero-copy write), and by the time it's live this
192                // `ActiveSend` has already been popped out of the
193                // scheduler's queue — so `poll_action` should never
194                // observe it. Wait rather than treat it as unreachable.
195                register_waker(&mut inner.driver_waker, cx.waker());
196                Poll::Pending
197            }
198        }
199    }
200
201    /// Installs a frame token whose real borrow is retained by the returned
202    /// lease. The token is only accessed while `inner` is locked.
203    ///
204    /// A fresh grant with nothing already staged (`buffer` empty — the
205    /// ordinary case, `state` is `Demand`) starts the zero-copy grace period
206    /// (`Granted`). A grant that arrives with `buffer` already non-empty
207    /// (`Fragment`/`FragmentDemand`, data staged from an earlier lease)
208    /// leaves `state` alone — there is nothing to wait for, `wait_fragment`
209    /// should drain it immediately.
210    pub(crate) unsafe fn grant<'a>(
211        shared: &Arc<Mutex<Self>>,
212        token: AnySend<'a>,
213        max_fragment_size: usize,
214    ) -> SendLease<'a> {
215        // SAFETY: `SendLease` retains the source mutable borrow and clears the
216        // token under the same mutex before that borrow ends.
217        let token = unsafe { std::mem::transmute::<AnySend<'a>, AnySend<'static>>(token) };
218        let mut inner = lock(shared);
219        assert!(inner.token.is_none());
220        if inner.buffer.is_empty() {
221            inner.state = SendState::Granted;
222        }
223        inner.token = Some(token);
224        inner.max_fragment_size = max_fragment_size;
225        let writer = inner.writer_waker.take();
226        drop(inner);
227        wake(writer);
228        SendLease {
229            shared: shared.clone(),
230            armed: true,
231            _borrow: PhantomData,
232        }
233    }
234
235    /// Waits for the next fragment/finish/abort decision, draining any
236    /// bytes the writer couldn't hand the transport synchronously.
237    ///
238    /// While `Granted`, gives the writer one cooperative scheduling turn to
239    /// show up and use the zero-copy fast path; if it doesn't, flips the
240    /// state to `Staging` (still holding the token) and keeps waiting — the
241    /// next `poll_write` will stage into `buffer` and wake this same wait
242    /// directly, with no further grant needed.
243    ///
244    /// Returns, alongside the action, whether the fragment that just
245    /// completed needed this draining at all (`false`) as opposed to having
246    /// been written entirely within the writer's own `poll_write` call
247    /// (`true`) — the same short-write signal `SendFrame::finish` reports,
248    /// used by the scheduler to adapt fragment sizing.
249    pub(crate) async fn wait_fragment(shared: &Mutex<Self>) -> io::Result<SendAction> {
250        let mut needed_drain = false;
251        let mut yielded = false;
252        loop {
253            let outcome = std::future::poll_fn(|cx| {
254                let mut inner = lock(shared);
255                inner.driver_waker.take();
256                if inner.state == SendState::Failed {
257                    let (kind, message) = inner.error.clone().expect("error set for Failed");
258                    return Poll::Ready(Err(io::Error::new(kind, message)));
259                }
260                if !inner.buffer.is_empty() {
261                    needed_drain = true;
262                    let result = poll_flush_buffer(&mut inner, cx);
263                    match result {
264                        Poll::Pending => return Poll::Pending,
265                        Poll::Ready(Err(error)) => {
266                            inner.state = SendState::Failed;
267                            inner.error = Some((error.kind(), error.to_string()));
268                            let writer = inner.writer_waker.take();
269                            drop(inner);
270                            wake(writer);
271                            return Poll::Ready(Err(error));
272                        }
273                        Poll::Ready(Ok(())) => {}
274                    }
275                }
276                match inner.state {
277                    SendState::Fragment | SendState::FragmentDemand | SendState::FragmentFinish => {
278                        Poll::Ready(Ok(Some(SendAction::Fragment)))
279                    }
280                    SendState::Finish => Poll::Ready(Ok(Some(SendAction::Finish))),
281                    SendState::Abort => Poll::Ready(Ok(Some(SendAction::Abort))),
282                    SendState::Granted if !yielded => {
283                        // One cooperative scheduling turn for the writer to
284                        // show up before we fall back to staging.
285                        Poll::Ready(Ok(None))
286                    }
287                    SendState::Granted => {
288                        inner.state = SendState::Staging;
289                        register_waker(&mut inner.driver_waker, cx.waker());
290                        Poll::Pending
291                    }
292                    SendState::Idle | SendState::Demand | SendState::Staging => {
293                        // Defensive: none of these should be observable
294                        // here (this function only runs while a lease from
295                        // `grant` is live), but wait rather than treat it
296                        // as unreachable.
297                        register_waker(&mut inner.driver_waker, cx.waker());
298                        Poll::Pending
299                    }
300                    SendState::Failed => unreachable!("handled above"),
301                }
302            })
303            .await?;
304            match outcome {
305                Some(result) => return Ok(result),
306                None => {
307                    yielded = true;
308                    tokio::task::yield_now().await;
309                }
310            }
311        }
312    }
313
314    fn poll_flush(shared: &Mutex<Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
315        let mut inner = lock(shared);
316        inner.writer_waker.take();
317        match inner.state {
318            SendState::Abort | SendState::Failed => {
319                let (kind, message) = inner.error.clone().expect("error set for Abort/Failed");
320                Poll::Ready(Err(io::Error::new(kind, message)))
321            }
322            _ if !inner.buffer.is_empty() => {
323                register_waker(&mut inner.writer_waker, cx.waker());
324                Poll::Pending
325            }
326            _ => Poll::Ready(Ok(())),
327        }
328    }
329
330    fn finish(shared: &Mutex<Self>) {
331        let mut inner = lock(shared);
332        inner.state = match inner.state {
333            SendState::Fragment | SendState::FragmentDemand => SendState::FragmentFinish,
334            SendState::FragmentFinish => SendState::FragmentFinish,
335            // Preserve an existing abort/failure verbatim rather than
336            // silently discarding it — `poll_write`/`poll_flush` must keep
337            // reporting the original error even if `finish` is (unusually)
338            // still called afterward.
339            aborted @ (SendState::Abort | SendState::Failed) => aborted,
340            _ => SendState::Finish,
341        };
342        let driver = inner.driver_waker.take();
343        let writer = inner.writer_waker.take();
344        drop(inner);
345        wake(driver);
346        wake(writer);
347    }
348
349    /// The writer dropped its `TrailerSend` without finishing.
350    fn abandon(shared: &Mutex<Self>) {
351        Self::set_aborted(shared, io::ErrorKind::BrokenPipe, "trailer is closed");
352    }
353
354    /// Cuts a still-open trailer send short from the scheduler's side:
355    /// records an error so the *live* `TrailerSend`'s writer observes a
356    /// clean failure on its next write instead of hanging, waiting for a
357    /// lease that will never come again. Used both for genuine cancellation
358    /// and for a peer-issued `Discard` notice — the two differ only in
359    /// whether the surrounding message as a whole is still considered
360    /// valid, which is a concern for the caller, not for this shared state.
361    /// Never observed by `wait_fragment`: both call sites remove or replace
362    /// the `ActiveSend`'s trailer before the scheduler could poll this
363    /// `SendShared` again.
364    pub(crate) fn discard(shared: &Mutex<Self>) {
365        Self::set_aborted(
366            shared,
367            io::ErrorKind::BrokenPipe,
368            "trailer discarded by peer",
369        );
370    }
371
372    fn set_aborted(shared: &Mutex<Self>, kind: io::ErrorKind, message: &str) {
373        let mut inner = lock(shared);
374        if !matches!(
375            inner.state,
376            SendState::Finish | SendState::FragmentFinish | SendState::Failed
377        ) {
378            inner.state = SendState::Abort;
379            inner.error = Some((kind, message.into()));
380        }
381        let driver = inner.driver_waker.take();
382        let writer = inner.writer_waker.take();
383        let session = inner.session.clone();
384        let id = inner.id;
385        drop(inner);
386        // No more credit will ever arrive for this trailer, so its share of
387        // the pool has to come back here or it is lost until the connection
388        // ends. `settle` is idempotent, so a `Credit` that crossed this on
389        // the wire cannot double-refund.
390        session.settle(id);
391        wake(driver);
392        wake(writer);
393    }
394
395    /// Reserves up to `want` bytes of pool credit, returning what was
396    /// granted. Zero means the pool is empty and the caller must park.
397    ///
398    /// Peer-returned credit is applied to the pool directly by the endpoint,
399    /// keyed by message id, rather than routed back through this trailer: a
400    /// `Credit` may well arrive after this `SendShared` is gone — see
401    /// [`SessionWindow`].
402    fn reserve(&mut self, want: usize) -> usize {
403        let granted = self.session.debit_up_to(self.id, want);
404        self.written += granted;
405        granted
406    }
407
408    /// Returns a reservation whose fragment was never committed.
409    fn unreserve(&mut self, len: usize) {
410        if len == 0 {
411            return;
412        }
413        self.written -= len;
414        self.session.refund(self.id, len);
415    }
416}
417
418/// Why `Granted`/`Staging` may assert they already hold a reservation: the
419/// scheduler only grants after `poll_action` reports `Fragment`, which
420/// `Idle` never does, so a fresh grant (the only kind that sets `Granted`,
421/// since `grant` leaves a non-empty `buffer`'s state alone) is always
422/// answering a `Demand` — and `Demand` is only entered from `Idle` with a
423/// reservation in hand.
424const RESERVED_HELD: &str = "Granted/Staging is only reached from Demand, which reserves";
425
426/// Parks the writer because the session pool is empty, registering it there
427/// so the matching refund wakes it.
428///
429/// This is only ever reached from `SendState::Idle`, so a starved
430/// trailer simply reports `Pending` from `poll_action` and the scheduler
431/// skips it. Parking in `Granted`/`Staging` instead would hold a live
432/// `SendLease` while the driver waited forever, wedging the connection's
433/// single writer.
434fn park_for_credit(shared: &mut SendShared, cx: &mut Context<'_>) {
435    // `writer_waker` covers abort and failure; the pool's own list covers the
436    // credit this park is actually waiting for.
437    register_waker(&mut shared.writer_waker, cx.waker());
438    shared.session.park(cx.waker());
439}
440
441fn poll_flush_buffer(shared: &mut SendShared, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
442    let Some(token) = shared.token.as_mut() else {
443        return Poll::Ready(Err(io::Error::other("send lease has no frame token")));
444    };
445    loop {
446        if shared.buffer.is_empty() {
447            break Poll::Ready(Ok(()));
448        }
449        match token.poll_write_once(cx, &shared.buffer) {
450            Poll::Ready(Ok(0)) => break Poll::Ready(Err(io::ErrorKind::WriteZero.into())),
451            Poll::Ready(Ok(n)) => shared.buffer.advance(n),
452            Poll::Ready(Err(error)) => break Poll::Ready(Err(error)),
453            Poll::Pending => break Poll::Pending,
454        }
455    }
456}
457
458pub(crate) struct SendLease<'a> {
459    shared: Arc<Mutex<SendShared>>,
460    armed: bool,
461    _borrow: PhantomData<&'a mut ()>,
462}
463
464impl SendLease<'_> {
465    pub(crate) fn complete(mut self) {
466        let mut shared = lock(&self.shared);
467        shared.token.take();
468        shared.buffer.clear();
469        shared.state = match shared.state {
470            SendState::Fragment | SendState::FragmentDemand => SendState::Idle,
471            SendState::FragmentFinish => SendState::Finish,
472            state => state,
473        };
474        let writer = shared.writer_waker.take();
475        self.armed = false;
476        drop(shared);
477        wake(writer);
478    }
479}
480
481impl Drop for SendLease<'_> {
482    fn drop(&mut self) {
483        if !self.armed {
484            return;
485        }
486        let mut shared = lock(&self.shared);
487        shared.token.take();
488        shared.buffer = BytesMut::new();
489        // Preserve an existing `Failed` (e.g. `wait_fragment` already
490        // observed a real I/O failure and this lease is being dropped
491        // uncompleted as a result) rather than downgrading it to a generic
492        // revocation message.
493        if shared.state != SendState::Failed {
494            shared.state = SendState::Abort;
495            if shared.error.is_none() {
496                shared.error = Some((
497                    io::ErrorKind::ConnectionAborted,
498                    "send grant was revoked".into(),
499                ));
500            }
501        }
502        let writer = shared.writer_waker.take();
503        shared.driver_waker.take();
504        drop(shared);
505        wake(writer);
506    }
507}
508
509/// Trailer send handle.
510///
511/// Call [`finish`](Self::finish) when done sending data;
512/// dropping it first aborts the trailer. `finish` returns
513/// an associated operation handle, such as a [`Call`](crate::client::Call).
514pub struct TrailerSend<T> {
515    shared: Arc<Mutex<SendShared>>,
516    completion: Option<T>,
517}
518
519impl<T> TrailerSend<T> {
520    pub(crate) fn new(shared: Arc<Mutex<SendShared>>, completion: T) -> Self {
521        Self {
522            shared,
523            completion: Some(completion),
524        }
525    }
526
527    /// Commits the trailer and returns the associated handle.
528    ///
529    /// This does not wait for buffered trailer bytes to reach the peer. Use
530    /// [`AsyncWriteExt::close`](futures::io::AsyncWriteExt::close) first
531    /// when that ordering matters to the caller.
532    pub fn finish(mut self) -> T {
533        SendShared::finish(&self.shared);
534        self.completion.take().unwrap()
535    }
536}
537
538impl<T: Unpin> AsyncWrite for TrailerSend<T> {
539    fn poll_write(
540        self: Pin<&mut Self>,
541        cx: &mut Context<'_>,
542        buf: &[u8],
543    ) -> Poll<io::Result<usize>> {
544        if buf.is_empty() {
545            return Poll::Ready(Ok(0));
546        }
547        let this = self.get_mut();
548        let mut inner = lock(&this.shared);
549        inner.writer_waker.take();
550        match inner.state {
551            SendState::Finish | SendState::FragmentFinish => Poll::Ready(Err(io::Error::new(
552                io::ErrorKind::BrokenPipe,
553                "trailer is closed",
554            ))),
555            SendState::Abort | SendState::Failed => {
556                let (kind, message) = inner.error.clone().expect("error set for Abort/Failed");
557                Poll::Ready(Err(io::Error::new(kind, message)))
558            }
559            SendState::Fragment | SendState::FragmentDemand => {
560                // A previously staged fragment hasn't been fully flushed
561                // yet — wait for the driver to drain it before staging (or
562                // writing) more. This is real backpressure (bounded to one
563                // fragment's worth of staged data), not a wait for the
564                // driver to win a scheduling race, so it's fine to block.
565                inner.state = SendState::FragmentDemand;
566                register_waker(&mut inner.writer_waker, cx.waker());
567                let driver = inner.driver_waker.take();
568                drop(inner);
569                wake(driver);
570                Poll::Pending
571            }
572            SendState::Idle => {
573                // Nothing may be reserved before the message itself has been
574                // admitted; see `SendShared::started`. Parking here rather
575                // than after the reservation is the whole point.
576                if !inner.started {
577                    register_waker(&mut inner.writer_waker, cx.waker());
578                    return Poll::Pending;
579                }
580                // The only place credit is ever waited for. Doing it here,
581                // before demanding a token, is what keeps a starved trailer
582                // from holding a transport grant hostage — see
583                // `park_for_credit`. The reservation taken here is *held*
584                // across `Demand`, so `Granted`/`Staging` always have credit
585                // in hand and never have to park while holding a lease. That
586                // matters now that the pool is the sole limiter: another
587                // trailer could otherwise drain it in between.
588                let want = buf.len().min(inner.max_fragment_size.max(1));
589                let len = inner.reserve(want);
590                if len == 0 {
591                    park_for_credit(&mut inner, cx);
592                    return Poll::Pending;
593                }
594                if len <= inner.copy_threshold {
595                    FragmentHeader {
596                        flags: Flags::NONE,
597                        kind: inner.kind,
598                        id: inner.id,
599                        payload_len: len,
600                    }
601                    .encode_into(&mut inner.buffer);
602                    inner.buffer.extend_from_slice(&buf[..len]);
603                    inner.state = SendState::Fragment;
604                    let driver = inner.driver_waker.take();
605                    drop(inner);
606                    wake(driver);
607                    return Poll::Ready(Ok(len));
608                }
609                // A large write asks to be granted a token for direct I/O,
610                // carrying its reservation along.
611                inner.reserved = len;
612                inner.state = SendState::Demand;
613                register_waker(&mut inner.writer_waker, cx.waker());
614                let driver = inner.driver_waker.take();
615                drop(inner);
616                wake(driver);
617                Poll::Pending
618            }
619            SendState::Demand => {
620                register_waker(&mut inner.writer_waker, cx.waker());
621                Poll::Pending
622            }
623            SendState::Staging => {
624                // The grace period for zero-copy already expired for this
625                // grant: stage directly and wake the driver, which is
626                // already waiting for exactly this.
627                debug_assert!(inner.reserved > 0, "{RESERVED_HELD}");
628                let len = buf
629                    .len()
630                    .min(inner.max_fragment_size.max(1))
631                    .min(inner.reserved);
632                let unused = inner.reserved - len;
633                inner.unreserve(unused);
634                inner.reserved = 0;
635                FragmentHeader {
636                    flags: Flags::NONE,
637                    kind: inner.kind,
638                    id: inner.id,
639                    payload_len: len,
640                }
641                .encode_into(&mut inner.buffer);
642                inner.buffer.extend_from_slice(&buf[..len]);
643                inner.state = SendState::Fragment;
644                let driver = inner.driver_waker.take();
645                drop(inner);
646                wake(driver);
647                Poll::Ready(Ok(len))
648            }
649            SendState::Granted => {
650                // Zero-copy fast path: a token is granted and waiting on
651                // us, so try writing directly instead of staging.
652                debug_assert!(inner.reserved > 0, "{RESERVED_HELD}");
653                let len = buf
654                    .len()
655                    .min(inner.max_fragment_size.max(1))
656                    .min(inner.reserved);
657                // Release the part of the reservation this fragment won't
658                // use, but keep `len` held: a `Pending` below retries against
659                // it rather than re-reserving against a pool that may have
660                // been drained meanwhile.
661                let unused = inner.reserved - len;
662                inner.unreserve(unused);
663                inner.reserved = len;
664                FragmentHeader {
665                    flags: Flags::NONE,
666                    kind: inner.kind,
667                    id: inner.id,
668                    payload_len: len,
669                }
670                .encode_into(&mut inner.buffer);
671
672                let header_len = inner.buffer.len();
673                let write_result = {
674                    let shared = &mut *inner;
675                    let bufs = [IoSlice::new(&shared.buffer), IoSlice::new(&buf[..len])];
676                    shared
677                        .token
678                        .as_mut()
679                        .expect("installed send token")
680                        .poll_write_vectored_once(cx, &bufs)
681                };
682                match write_result {
683                    Poll::Ready(Ok(0)) => {
684                        let error = io::Error::from(io::ErrorKind::WriteZero);
685                        inner.buffer.clear();
686                        inner.unreserve(len);
687                        inner.reserved = 0;
688                        inner.state = SendState::Failed;
689                        inner.error = Some((error.kind(), error.to_string()));
690                        let driver = inner.driver_waker.take();
691                        drop(inner);
692                        wake(driver);
693                        Poll::Ready(Err(error))
694                    }
695                    Poll::Ready(Ok(n)) => {
696                        debug_assert!(n <= header_len + len);
697                        if n < header_len {
698                            inner.buffer.advance(n);
699                            inner.buffer.extend_from_slice(&buf[..len]);
700                        } else {
701                            inner.buffer.clear();
702                            inner.buffer.extend_from_slice(&buf[n - header_len..len]);
703                        }
704                        inner.reserved = 0;
705                        inner.state = SendState::Fragment;
706                        let driver = inner.driver_waker.take();
707                        drop(inner);
708                        wake(driver);
709                        Poll::Ready(Ok(len))
710                    }
711                    Poll::Ready(Err(error)) => {
712                        inner.buffer.clear();
713                        inner.unreserve(len);
714                        inner.reserved = 0;
715                        inner.state = SendState::Failed;
716                        inner.error = Some((error.kind(), error.to_string()));
717                        let driver = inner.driver_waker.take();
718                        drop(inner);
719                        wake(driver);
720                        Poll::Ready(Err(error))
721                    }
722                    Poll::Pending => {
723                        // No header bytes were committed, so this write
724                        // remains entirely the caller's and can be retried
725                        // with a new buffer. The reservation stays held for
726                        // that retry.
727                        inner.buffer.clear();
728                        Poll::Pending
729                    }
730                }
731            }
732        }
733    }
734
735    fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
736        SendShared::poll_flush(&self.get_mut().shared, cx)
737    }
738
739    fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<io::Result<()>> {
740        let this = self.get_mut();
741        SendShared::finish(&this.shared);
742        SendShared::poll_flush(&this.shared, cx)
743    }
744}
745
746impl<T> Drop for TrailerSend<T> {
747    fn drop(&mut self) {
748        if self.completion.is_some() {
749            SendShared::abandon(&self.shared);
750        }
751    }
752}
753
754#[derive(Clone, Copy, Debug, Eq, PartialEq)]
755enum RecvState {
756    Idle,
757    /// The consumer polled while no fragment was granted and is waiting for
758    /// the next fragment specifically.
759    Demand,
760    /// A fragment is granted and a zero-copy read returned `Pending`, so the
761    /// transport registered the consumer's waker. `wait_fragment` trusts
762    /// that registration instead of taking over draining itself.
763    Reading,
764    /// A fragment is granted, but nothing yet guarantees the consumer will
765    /// be polled again — either it hasn't asked for this fragment at all
766    /// yet, or an earlier read happened to exactly satisfy the previous
767    /// fragment and it never came back for this one. `wait_fragment` gives
768    /// this state exactly one cooperative scheduling turn to resolve into
769    /// `Reading` on its own before falling back to `Draining`.
770    Unclaimed,
771    /// The driver has taken over pulling the remainder of the fragment off
772    /// the wire into `stage` — reached either from `Unclaimed` (grace
773    /// turn passed uneventfully) or directly from `Reading`/`Unclaimed`
774    /// when a consumer's own zero-copy read came up short and didn't ask
775    /// for more. The consumer now only reads from `stage`.
776    Draining,
777    Fragment,
778    /// The current fragment is complete, but its lease has not yet been
779    /// released and the consumer has already polled for the next fragment.
780    FragmentDemand,
781    Eof,
782    Discard,
783    /// A read failed, or the grant/connection was aborted/revoked. `error`
784    /// holds the `io::Error` to report; `state` is authoritative for
785    /// whether it's set, never checked independently.
786    Failed,
787}
788
789pub(crate) struct RecvShared {
790    token: Option<AnyRecv<'static>>,
791    remaining: usize,
792    /// Bytes the driver pulled off the wire on the consumer's behalf while
793    /// in `RecvState::Draining` (or discarded on the wire but not yet
794    /// consumed via `RecvState::Discard`). Always drained to the consumer
795    /// before anything else — see `TrailerRecv::poll_read`.
796    stage: BytesMut,
797    copy_threshold: usize,
798    demand_copy_threshold: usize,
799    state: RecvState,
800    /// Set exactly when `state` is `Failed`, cleared never.
801    error: Option<(io::ErrorKind, String)>,
802    reader_waker: Option<Waker>,
803    driver_waker: Option<Waker>,
804    /// How much retired credit accumulates before a `Credit` goes out.
805    /// Purely local coalescing granularity — what actually bounds the peer is
806    /// the session pool, not this.
807    credit_interval: usize,
808    /// Cumulative trailer bytes accepted from the wire, and cumulative bytes
809    /// retired (already credited, plus `pending`). Kept only to catch a
810    /// manual consumer releasing more than it was given.
811    received: usize,
812    retired: usize,
813    /// Retired but not yet sent as a `Credit`. See `retire`.
814    pending: usize,
815    /// When false, the consumer releases credit explicitly via
816    /// [`TrailerRecv::release`] instead of implicitly on read.
817    auto_release: bool,
818    /// The route back to the peer, for the `Credit`/`Discard` fragments this
819    /// trailer sends on its own behalf.
820    sink: Arc<dyn ControlSink>,
821    /// The message id those fragments name.
822    id: u64,
823    /// The receive-side session pool, so `accept_bytes` can check the
824    /// aggregate bound and retirement can give back to it.
825    session: Arc<SessionWindow>,
826}
827
828impl RecvShared {
829    pub(crate) fn new(
830        copy_threshold: usize,
831        demand_copy_threshold: usize,
832        credit_interval: usize,
833        session: Arc<SessionWindow>,
834        id: u64,
835        sink: Arc<dyn ControlSink>,
836    ) -> Arc<Mutex<Self>> {
837        Arc::new(Mutex::new(Self {
838            token: None,
839            remaining: 0,
840            stage: BytesMut::new(),
841            copy_threshold,
842            demand_copy_threshold,
843            state: RecvState::Idle,
844            error: None,
845            reader_waker: None,
846            driver_waker: None,
847            credit_interval,
848            received: 0,
849            retired: 0,
850            pending: 0,
851            auto_release: true,
852            sink,
853            id,
854            session,
855        }))
856    }
857
858    /// Accounts `len` bytes arriving from the wire against the session pool,
859    /// returning a reason if the peer overran the credit it was granted.
860    ///
861    /// This is the backstop that makes the memory bound hold against a peer
862    /// that ignores the credit it agreed to; a well-behaved one never trips
863    /// it. The pool is the only bound — there is no per-trailer window to
864    /// check, because a sender over-committing one of its own trailers can
865    /// only starve its other trailers, never this end.
866    pub(crate) fn accept_bytes(shared: &Mutex<Self>, len: usize) -> Option<&'static str> {
867        let mut inner = lock(shared);
868        // A discarded trailer's bytes are sunk as they arrive and never
869        // held, so the bound does not apply — there is nothing to bound, and
870        // the peer is racing a `Discard` it has not seen yet.
871        if inner.state == RecvState::Discard {
872            return None;
873        }
874        if !inner.session.accept_bytes(inner.id, len) {
875            return Some("exceeded the session trailer credit window");
876        }
877        inner.received += len;
878        None
879    }
880
881    /// Records `len` bytes as retired and emits a `Credit` if that crosses
882    /// the coalescing threshold.
883    ///
884    /// Emission happens at half a `credit_interval`, *or* whenever holding
885    /// credit back could be what keeps the peer parked: the trailer has
886    /// ended, the pool is exhausted, or this trailer's own consumer is
887    /// blocked waiting for bytes that are not coming. None of those are
888    /// optimisations — without them a consumer that retires less than the
889    /// interval and then waits for more data deadlocks against a sender that
890    /// is waiting for exactly the credit sitting in `pending`.
891    fn retire(inner: &mut Self, len: usize) -> Option<(Arc<dyn ControlSink>, u64, u32)> {
892        // `discard` already returned this trailer's whole outstanding debt
893        // to the pool in one go. Bytes retired after that point — `poll_read`
894        // still serves whatever was left in `stage`, and `discard` does not
895        // consume the handle — must not be refunded a second time.
896        if inner.state == RecvState::Discard {
897            return None;
898        }
899        inner.pending += len;
900        inner.retired += len;
901        if inner.pending * 2 < inner.credit_interval && !Self::must_flush(inner) {
902            return None;
903        }
904        Self::flush(inner)
905    }
906
907    /// Whether coalescing must give way, because credit held back here could
908    /// be the only thing the peer is missing.
909    ///
910    /// The first two clauses are session-scoped: a finished trailer would
911    /// otherwise strand its pool debt for the life of the connection, and a
912    /// drained pool means the peer is provably parked. The third is
913    /// trailer-scoped and is what lets a sender divide the pool among its own
914    /// trailers however it likes: a private per-trailer budget is invisible
915    /// from here, but a sender parked on one produces the same symptom as any
916    /// other stall — this consumer asking for bytes that never arrive. Since
917    /// `credit_interval` is a purely local knob neither end advertises, this
918    /// is the only way a sender's subdivision can be safe without the two
919    /// ends agreeing on a number.
920    fn must_flush(inner: &Self) -> bool {
921        inner.state == RecvState::Eof || inner.session.is_exhausted() || Self::is_stalled(inner)
922    }
923
924    /// Whether the consumer is parked with nothing left to read: it has
925    /// demanded a fragment that has not arrived, and `stage` is empty.
926    fn is_stalled(inner: &Self) -> bool {
927        matches!(inner.state, RecvState::Demand | RecvState::FragmentDemand)
928            && inner.stage.is_empty()
929    }
930
931    /// Emits whatever credit has accumulated, whatever the threshold says.
932    fn flush(inner: &mut Self) -> Option<(Arc<dyn ControlSink>, u64, u32)> {
933        if inner.state == RecvState::Discard || inner.pending == 0 {
934            return None;
935        }
936        let count = u32::try_from(inner.pending).unwrap_or(u32::MAX) as usize;
937        inner.pending -= count;
938        inner.session.refund(inner.id, count);
939        Some((inner.sink.clone(), inner.id, count as u32))
940    }
941
942    /// `retire` plus the send, for callers that hold the guard and can drop
943    /// it first. The sink is never called under the mutex.
944    fn retire_and_emit(shared: &Mutex<Self>, len: usize) {
945        let mut inner = lock(shared);
946        if !inner.auto_release {
947            return;
948        }
949        let emit = Self::retire(&mut inner, len);
950        drop(inner);
951        if let Some((sink, id, count)) = emit {
952            sink.credit(id, count);
953        }
954    }
955
956    /// Installs a fresh fragment and selects copying or rendezvous according
957    /// to whether the consumer demanded this specific fragment.
958    pub(crate) unsafe fn grant<'a>(
959        shared: &Arc<Mutex<Self>>,
960        token: AnyRecv<'a>,
961        remaining: usize,
962    ) -> RecvLease<'a> {
963        // SAFETY: `RecvLease` retains the source mutable borrow and clears
964        // the token under the same mutex before that borrow ends.
965        let token = unsafe { std::mem::transmute::<AnyRecv<'a>, AnyRecv<'static>>(token) };
966        let mut inner = lock(shared);
967        assert!(inner.token.is_none());
968        if inner.state != RecvState::Discard {
969            let demanded = inner.state == RecvState::Demand;
970            let copy_threshold = if demanded {
971                inner.demand_copy_threshold
972            } else {
973                inner.copy_threshold
974            };
975            inner.state = if remaining == 0 {
976                if demanded {
977                    RecvState::FragmentDemand
978                } else {
979                    RecvState::Fragment
980                }
981            } else if remaining <= copy_threshold {
982                RecvState::Draining
983            } else {
984                RecvState::Unclaimed
985            };
986        }
987        inner.token = Some(token);
988        inner.remaining = remaining;
989        let reader = if inner.state == RecvState::Unclaimed {
990            inner.reader_waker.take()
991        } else {
992            None
993        };
994        drop(inner);
995        wake(reader);
996        RecvLease {
997            shared: shared.clone(),
998            armed: true,
999            _borrow: PhantomData,
1000        }
1001    }
1002
1003    /// Waits for the current fragment to be fully off the wire, driving the
1004    /// actual transport reads itself whenever the consumer isn't (state
1005    /// `Draining` or `Discard`) — see `TrailerRecv::poll_read` for how a
1006    /// consumer hands off to this. This is what guarantees forward
1007    /// progress independent of whether (or how promptly) the consumer
1008    /// polls: this function is only ever driven by the connection's single
1009    /// receiver loop, which is always being polled as long as the
1010    /// connection is alive.
1011    ///
1012    /// Returns `Ok(true)` if the trailer was discarded.
1013    pub(crate) async fn wait_fragment(shared: &Mutex<Self>) -> io::Result<bool> {
1014        // Persists across multiple polls of the single `poll_fn` future
1015        // below (for as long as this `wait_fragment` call remains
1016        // unresolved), same as a struct field would, but scoped to this
1017        // one grant with no separate reset needed.
1018        let mut grace_given = false;
1019        std::future::poll_fn(|cx| {
1020            let mut inner = lock(shared);
1021            inner.driver_waker.take();
1022            loop {
1023                match inner.state {
1024                    RecvState::Fragment | RecvState::FragmentDemand => {
1025                        return Poll::Ready(Ok(false));
1026                    }
1027                    RecvState::Discard if inner.remaining == 0 => return Poll::Ready(Ok(true)),
1028                    RecvState::Draining | RecvState::Discard => {}
1029                    RecvState::Reading => {
1030                        // Something already guarantees a future poll —
1031                        // trust it to make progress and call back.
1032                        register_waker(&mut inner.driver_waker, cx.waker());
1033                        return Poll::Pending;
1034                    }
1035                    RecvState::Unclaimed => {
1036                        if !grace_given {
1037                            // Nothing guarantees the consumer will call
1038                            // `poll_read` again (e.g. it hasn't asked for
1039                            // this fragment yet, or a buffered reader's
1040                            // read happened to land exactly on the previous
1041                            // fragment boundary). Give it one cooperative
1042                            // scheduling turn to show up on its own before
1043                            // taking over.
1044                            grace_given = true;
1045                            cx.waker().wake_by_ref();
1046                            return Poll::Pending;
1047                        }
1048                        inner.state = RecvState::Draining;
1049                    }
1050                    RecvState::Idle | RecvState::Demand | RecvState::Eof => {
1051                        register_waker(&mut inner.driver_waker, cx.waker());
1052                        return Poll::Pending;
1053                    }
1054                    RecvState::Failed => {
1055                        // Defensive: `fail`/`RecvLease::Drop` only ever set
1056                        // this at a point where this function isn't
1057                        // concurrently driving the same `RecvShared` (see
1058                        // their doc comments), so this should be
1059                        // unreachable — wait rather than treat it as such.
1060                        register_waker(&mut inner.driver_waker, cx.waker());
1061                        return Poll::Pending;
1062                    }
1063                }
1064                let discard = inner.state == RecvState::Discard;
1065                let result = if discard {
1066                    let mut sink = [0u8; 8192];
1067                    let n = inner.remaining.min(sink.len());
1068                    let mut dest = &mut sink[..n];
1069                    inner
1070                        .token
1071                        .as_mut()
1072                        .expect("installed receive token")
1073                        .poll_read_once(cx, &mut dest)
1074                } else {
1075                    let remaining = inner.remaining;
1076                    inner.stage.reserve(remaining);
1077                    let RecvShared { token, stage, .. } = &mut *inner;
1078                    // `stage` may have more spare capacity than `remaining`
1079                    // left over from an earlier, larger fragment — cap the
1080                    // read so a transport with more already-buffered bytes
1081                    // available (e.g. the next fragment, already sitting in
1082                    // the OS receive buffer) can't overrun this fragment's
1083                    // boundary.
1084                    let mut limited = stage.limit(remaining);
1085                    token
1086                        .as_mut()
1087                        .expect("installed receive token")
1088                        .poll_read_once(cx, &mut limited)
1089                };
1090                match result {
1091                    Poll::Ready(Ok(0)) => {
1092                        return Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into()));
1093                    }
1094                    Poll::Ready(Ok(n)) => {
1095                        inner.remaining -= n;
1096                        if inner.remaining == 0 && inner.state == RecvState::Draining {
1097                            inner.state = RecvState::Fragment;
1098                        }
1099                        if !discard {
1100                            let reader = inner.reader_waker.take();
1101                            wake(reader);
1102                        }
1103                        // Loop back around: reassess state (may now be
1104                        // `Fragment`/still-zero `Discard`) or keep draining.
1105                    }
1106                    Poll::Ready(Err(error)) => return Poll::Ready(Err(error)),
1107                    Poll::Pending => {
1108                        register_waker(&mut inner.driver_waker, cx.waker());
1109                        return Poll::Pending;
1110                    }
1111                }
1112            }
1113        })
1114        .await
1115    }
1116
1117    /// Marks the trailer complete, flushing any credit coalescing was still
1118    /// holding back.
1119    ///
1120    /// That flush is not an optimisation. A trailer smaller than the
1121    /// coalescing threshold retires every one of its bytes below it, so
1122    /// nothing has been credited when it ends — and once it has ended
1123    /// nothing more can trigger a flush: no further bytes arrive, and a
1124    /// completed trailer sends no `Discard` for the peer to settle against.
1125    /// The peer's matching pool debt would then be stranded for the life of
1126    /// the connection, and a stream of small trailers would exhaust its
1127    /// session window and park it forever.
1128    pub(crate) fn finish(shared: &Mutex<Self>) {
1129        let mut inner = lock(shared);
1130        inner.state = RecvState::Eof;
1131        // Retiring nothing, purely to take the `Eof` flush path.
1132        let emit = Self::retire(&mut inner, 0);
1133        let reader = inner.reader_waker.take();
1134        drop(inner);
1135        wake(reader);
1136        if let Some((sink, id, count)) = emit {
1137            sink.credit(id, count);
1138        }
1139    }
1140
1141    pub(crate) fn fail(shared: &Mutex<Self>, error: io::Error) {
1142        let mut inner = lock(shared);
1143        inner.state = RecvState::Failed;
1144        inner.error = Some((error.kind(), error.to_string()));
1145        let reader = inner.reader_waker.take();
1146        drop(inner);
1147        wake(reader);
1148    }
1149
1150    /// Stops wanting this trailer, and tells the peer so immediately.
1151    ///
1152    /// The notice has to be eager. A sender parked on exhausted credit emits
1153    /// no further fragments, so the old trigger — noticing that another
1154    /// `TRAILER` fragment arrived unwanted — can never fire, and an
1155    /// abandoned trailer would leave its sender parked forever instead of
1156    /// aborting it. With manual release the ambiguity is worse still: "the
1157    /// consumer is holding bytes it has not released" and "the consumer is
1158    /// gone" look identical from the sender's side, and this is the only
1159    /// thing that distinguishes them.
1160    ///
1161    /// Idempotent, and safe after the trailer has already finished — a
1162    /// `Discard` for a completed trailer is a defined no-op on the peer.
1163    pub(crate) fn discard(shared: &Mutex<Self>) {
1164        let mut inner = lock(shared);
1165        // Entering `Discard` is what makes this idempotent: a consumer that
1166        // calls `discard()` and then drops the handle must not send two
1167        // notices, nor refund the pool twice.
1168        if inner.state == RecvState::Discard {
1169            return;
1170        }
1171        // A trailer that already ended has nothing to stop, so dropping its
1172        // handle — the overwhelmingly common case — stays silent. The pool
1173        // settlement below still runs.
1174        let ended = inner.state == RecvState::Eof;
1175        if !matches!(inner.state, RecvState::Eof | RecvState::Failed) {
1176            inner.state = RecvState::Discard;
1177        }
1178        let driver = inner.driver_waker.take();
1179        let notify = (!ended).then(|| (inner.sink.clone(), inner.id));
1180        // Whatever this trailer still owes the pool will never be retired
1181        // now, so hand it back in one go rather than stranding it for the
1182        // life of the connection.
1183        inner.pending = 0;
1184        inner.retired = inner.received;
1185        let session = inner.session.clone();
1186        let id = inner.id;
1187        drop(inner);
1188        session.settle(id);
1189        wake(driver);
1190        if let Some((sink, id)) = notify {
1191            sink.discard(id);
1192        }
1193    }
1194}
1195
1196pub(crate) struct RecvLease<'a> {
1197    shared: Arc<Mutex<RecvShared>>,
1198    armed: bool,
1199    _borrow: PhantomData<&'a mut ()>,
1200}
1201
1202impl RecvLease<'_> {
1203    pub(crate) fn complete(mut self) {
1204        let mut shared = lock(&self.shared);
1205        shared.token.take();
1206        shared.remaining = 0;
1207        shared.state = match shared.state {
1208            RecvState::Fragment => RecvState::Idle,
1209            RecvState::FragmentDemand => RecvState::Demand,
1210            state => state,
1211        };
1212        self.armed = false;
1213    }
1214}
1215
1216impl Drop for RecvLease<'_> {
1217    fn drop(&mut self) {
1218        if !self.armed {
1219            return;
1220        }
1221        let mut shared = lock(&self.shared);
1222        shared.token.take();
1223        shared.remaining = 0;
1224        // Preserve an existing error (e.g. `fail` already ran for an
1225        // earlier ABORT on this message) rather than downgrading it to a
1226        // generic revocation message.
1227        shared.state = RecvState::Failed;
1228        if shared.error.is_none() {
1229            shared.error = Some((
1230                io::ErrorKind::ConnectionAborted,
1231                "receive grant was revoked".into(),
1232            ));
1233        }
1234        let reader = shared.reader_waker.take();
1235        shared.driver_waker.take();
1236        drop(shared);
1237        wake(reader);
1238    }
1239}
1240
1241/// Trailer receive handle.
1242pub struct TrailerRecv {
1243    pub(crate) shared: Arc<Mutex<RecvShared>>,
1244}
1245
1246impl TrailerRecv {
1247    pub(crate) fn new(shared: Arc<Mutex<RecvShared>>) -> Self {
1248        Self { shared }
1249    }
1250
1251    /// Switches this trailer to manual credit release, before it is handed
1252    /// to the application.
1253    ///
1254    /// Not public: the mode is chosen where the trailer is obtained, so
1255    /// there is no way to switch one mid-stream and no half-credited state
1256    /// to reason about. See [`release`](Self::release) for the rules manual
1257    /// mode imposes on the consumer.
1258    pub(crate) fn set_manual_credit(&mut self) {
1259        let mut inner = lock(&self.shared);
1260        debug_assert!(
1261            inner.retired == 0,
1262            "manual credit must be selected before the first read"
1263        );
1264        inner.auto_release = false;
1265    }
1266
1267    /// Returns `n` bytes of credit to the peer.
1268    ///
1269    /// Only meaningful on a trailer obtained in manual-credit mode — via
1270    /// [`CallContext::trailer_manual_credit`] or
1271    /// [`CallResult::into_response_trailer_manual_credit`].
1272    ///
1273    /// # Deadlocks
1274    ///
1275    /// Never attempt to receive multiple chunks or a fixed amount of data
1276    /// (e.g. via [`AsyncReadExt::read_exact`](futures::io::AsyncReadExt::read_exact))
1277    /// between credit releases, or a deadlock may result.
1278    ///
1279    /// # Panics
1280    ///
1281    /// In debug builds, if called on an automatic trailer, or if the running
1282    /// total exceeds the bytes actually delivered.
1283    ///
1284    /// [`CallContext::trailer_manual_credit`]: crate::server::CallContext::trailer_manual_credit
1285    /// [`CallResult::into_response_trailer_manual_credit`]: crate::client::CallResult::into_response_trailer_manual_credit
1286    pub fn release(&mut self, n: usize) {
1287        let mut inner = lock(&self.shared);
1288        debug_assert!(
1289            !inner.auto_release,
1290            "release requires a manual-credit trailer; credit is returned on read otherwise"
1291        );
1292        debug_assert!(
1293            inner.retired + n <= inner.received,
1294            "released more trailer credit than was delivered"
1295        );
1296        if n == 0 {
1297            return;
1298        }
1299        let emit = RecvShared::retire(&mut inner, n);
1300        drop(inner);
1301        if let Some((sink, id, count)) = emit {
1302            sink.credit(id, count);
1303        }
1304    }
1305}
1306
1307impl TrailerRecv {
1308    /// Reads into storage that need not be initialized, as
1309    /// [`AsyncRead::poll_read`] does into storage that is.
1310    ///
1311    /// Only initialized bytes are written, at the front of `buf`; the count
1312    /// is returned, and zero means the trailer has ended.
1313    pub fn poll_read_uninit(
1314        &mut self,
1315        cx: &mut Context<'_>,
1316        mut buf: &mut [MaybeUninit<u8>],
1317    ) -> Poll<io::Result<usize>> {
1318        if buf.is_empty() {
1319            return Poll::Ready(Ok(0));
1320        }
1321        let mut inner = lock(&self.shared);
1322        inner.reader_waker.take();
1323        // Bytes the driver already pulled off the wire on our behalf (see
1324        // `RecvState::Draining` below) always go out first, ahead of even a
1325        // recorded error or EOF — they were legitimately received and the
1326        // error/EOF is only discovered on the poll after they run out.
1327        //
1328        // Unlike `token`/`remaining`, whether `stage` has a backlog isn't
1329        // implied by `state`: the driver deliberately doesn't wait for us
1330        // to drain it before completing this lease and granting the next
1331        // fragment (that's the whole point of `Draining` — it lets the
1332        // driver keep pipelining ahead of a slow or absent reader), so
1333        // `state` can already describe a *later* fragment's grant while
1334        // `stage` still holds an *earlier* fragment's undelivered tail.
1335        // These are two genuinely independent facts, so `stage` has to be
1336        // checked directly rather than folded into `state`.
1337        if !inner.stage.is_empty() {
1338            let n = buf.len().min(inner.stage.len());
1339            buf.put_slice(&inner.stage[..n]);
1340            let _ = inner.stage.split_to(n);
1341            // One of the two points where bytes actually reach the
1342            // application, and so one of the two places auto-release retires
1343            // them. Drop the guard first: the sink is never called under the
1344            // mutex.
1345            drop(inner);
1346            RecvShared::retire_and_emit(&self.shared, n);
1347            return Poll::Ready(Ok(n));
1348        }
1349        match inner.state {
1350            RecvState::Failed => {
1351                let (kind, message) = inner.error.clone().expect("error set for Failed");
1352                Poll::Ready(Err(io::Error::new(kind, message)))
1353            }
1354            RecvState::Eof => Poll::Ready(Ok(0)),
1355            RecvState::Idle | RecvState::Fragment => {
1356                inner.state = if inner.state == RecvState::Idle {
1357                    RecvState::Demand
1358                } else {
1359                    RecvState::FragmentDemand
1360                };
1361                register_waker(&mut inner.reader_waker, cx.waker());
1362                // The consumer has just gone from reading to waiting, with
1363                // nothing staged behind it. Coalescing has nothing left to
1364                // gain here and could cost everything: if the peer is parked
1365                // — on the pool, or on a per-trailer budget of its own that
1366                // this end cannot see — the credit sitting in `pending` is
1367                // exactly what would release it. `retire` covers credit
1368                // retired *while* stalled; this covers credit already
1369                // accumulated when the stall begins.
1370                let emit = RecvShared::flush(&mut inner);
1371                drop(inner);
1372                if let Some((sink, id, count)) = emit {
1373                    sink.credit(id, count);
1374                }
1375                Poll::Pending
1376            }
1377            RecvState::Demand | RecvState::FragmentDemand => {
1378                register_waker(&mut inner.reader_waker, cx.waker());
1379                Poll::Pending
1380            }
1381            RecvState::Draining | RecvState::Discard => {
1382                // The driver owns pulling bytes off the wire, so `stage`
1383                // (checked above) is the only thing we can serve from until
1384                // it is refilled or the fragment/trailer completes.
1385                register_waker(&mut inner.reader_waker, cx.waker());
1386                Poll::Pending
1387            }
1388            RecvState::Reading | RecvState::Unclaimed => {
1389                // Zero-copy path: read directly into the caller's buffer.
1390                // Both states guarantee a live token and `remaining > 0`
1391                // (set together by `grant`).
1392                let mut limited = (&mut buf).limit(inner.remaining);
1393                let result = inner
1394                    .token
1395                    .as_mut()
1396                    .expect("installed receive token")
1397                    .poll_read_once(cx, &mut limited);
1398                match result {
1399                    Poll::Ready(Ok(0)) => Poll::Ready(Err(io::ErrorKind::UnexpectedEof.into())),
1400                    Poll::Ready(Ok(n)) => {
1401                        inner.remaining -= n;
1402                        if inner.remaining == 0 {
1403                            inner.state = RecvState::Fragment;
1404                        } else {
1405                            // A short read here says nothing about whether
1406                            // the consumer intends to ask for more soon — a
1407                            // buffered reader upstream (e.g. the zip
1408                            // crate's `BufReader`) routinely over-requests
1409                            // for read-ahead, then goes quiet once its
1410                            // immediate caller is satisfied, potentially
1411                            // forever. Hand the token off to the driver
1412                            // (`wait_fragment`), which is always being
1413                            // polled independent of this consumer and can
1414                            // therefore be relied on to finish draining the
1415                            // fragment into `stage` regardless. Leaving the
1416                            // remainder on the wire would instead stall the
1417                            // connection's single sequential reader — which
1418                            // must fully drain this fragment before it can
1419                            // read the *next* fragment, for any message —
1420                            // on a consumer poll that may never come,
1421                            // wedging every other in-flight call on the
1422                            // connection.
1423                            inner.state = RecvState::Draining;
1424                        }
1425                        let driver = inner.driver_waker.take();
1426                        drop(inner);
1427                        wake(driver);
1428                        // The other delivery point; see the `stage` branch.
1429                        RecvShared::retire_and_emit(&self.shared, n);
1430                        Poll::Ready(Ok(n))
1431                    }
1432                    Poll::Ready(Err(error)) => Poll::Ready(Err(error)),
1433                    Poll::Pending => {
1434                        // The transport registered its own readiness waker
1435                        // for this task, so the consumer will be polled
1436                        // again once more data arrives — `wait_fragment`
1437                        // can trust that.
1438                        inner.state = RecvState::Reading;
1439                        Poll::Pending
1440                    }
1441                }
1442            }
1443        }
1444    }
1445}
1446
1447impl AsyncRead for TrailerRecv {
1448    fn poll_read(
1449        self: Pin<&mut Self>,
1450        cx: &mut Context<'_>,
1451        buf: &mut [u8],
1452    ) -> Poll<io::Result<usize>> {
1453        // SAFETY: `poll_read_uninit` writes only initialized bytes.
1454        let buf = unsafe { &mut *(buf as *mut [u8] as *mut [MaybeUninit<u8>]) };
1455        self.get_mut().poll_read_uninit(cx, buf)
1456    }
1457}
1458
1459impl Drop for TrailerRecv {
1460    fn drop(&mut self) {
1461        RecvShared::discard(&self.shared);
1462    }
1463}
1464
1465#[cfg(test)]
1466mod tests {
1467    use super::*;
1468    use crate::transport::{AnyReceiver, AnySender, Receiver, Sender, generic};
1469
1470    /// Limits whose credit pool never binds, for tests about anything other
1471    /// than flow control.
1472    fn unbounded_limits() -> Limits {
1473        Limits {
1474            trailer_session_window: usize::MAX,
1475            ..zero_copy_limits()
1476        }
1477    }
1478
1479    /// Limits under which every write is large enough to demand a token
1480    /// instead of staging, so a test can drive the zero-copy path with
1481    /// conveniently small buffers.
1482    fn zero_copy_limits() -> Limits {
1483        Limits {
1484            trailer_send_copy_threshold: 0,
1485            ..Limits::default()
1486        }
1487    }
1488
1489    /// Drives a writer to the only state the scheduler ever grants from: a
1490    /// `Demand`, which is where the write's credit reservation is taken.
1491    /// Granting straight out of `Idle` would hand back a token the writer
1492    /// never asked for and skip the reservation with it.
1493    fn demand<T: Unpin>(trailer: &mut TrailerSend<T>, buf: &[u8]) {
1494        let mut cx = Context::from_waker(Waker::noop());
1495        assert!(
1496            Pin::new(trailer).poll_write(&mut cx, buf).is_pending(),
1497            "a write past the copy threshold must demand a token"
1498        );
1499    }
1500
1501    fn send_shared(limits: Limits) -> Arc<Mutex<SendShared>> {
1502        send_shared_id(1, limits)
1503    }
1504
1505    fn send_shared_id(id: u64, limits: Limits) -> Arc<Mutex<SendShared>> {
1506        let session = Arc::new(SessionWindow::new(limits.trailer_session_window));
1507        let shared = SendShared::new(Kind::Request, id, &limits, session);
1508        // These tests stand in for a message the scheduler has already
1509        // admitted; the unstarted case has its own test below.
1510        SendShared::start(&shared);
1511        shared
1512    }
1513
1514    /// A `RecvShared` with a sink that records what it would have sent, so
1515    /// credit and discard decisions can be asserted directly.
1516    #[derive(Default)]
1517    struct RecordingSink {
1518        credits: Mutex<Vec<(u64, u32)>>,
1519        discards: Mutex<Vec<u64>>,
1520    }
1521
1522    impl ControlSink for Arc<RecordingSink> {
1523        fn payload_credit(&self, _count: u32) {
1524            unreachable!("trailer tests never release payload quota")
1525        }
1526
1527        fn credit(&self, id: u64, count: u32) {
1528            lock(&self.credits).push((id, count));
1529        }
1530
1531        fn discard(&self, id: u64) {
1532            lock(&self.discards).push(id);
1533        }
1534    }
1535
1536    fn recv_shared(limits: Limits) -> (Arc<Mutex<RecvShared>>, Arc<RecordingSink>) {
1537        let session = Arc::new(SessionWindow::new(limits.trailer_session_window));
1538        let sink = Arc::new(RecordingSink::default());
1539        let shared = RecvShared::new(
1540            limits.trailer_recv_copy_threshold,
1541            limits.trailer_recv_demand_copy_threshold,
1542            limits.trailer_credit_interval,
1543            session,
1544            7,
1545            Arc::new(sink.clone()),
1546        );
1547        (shared, sink)
1548    }
1549
1550    fn poll_read_once(trailer: &mut TrailerRecv, output: &mut [u8]) -> Poll<io::Result<usize>> {
1551        let mut cx = Context::from_waker(Waker::noop());
1552        Pin::new(trailer).poll_read(&mut cx, output)
1553    }
1554
1555    struct CappedSink {
1556        bytes: Arc<Mutex<Vec<u8>>>,
1557        max_write: usize,
1558    }
1559
1560    impl AsyncWrite for CappedSink {
1561        fn poll_write(
1562            self: Pin<&mut Self>,
1563            _cx: &mut Context<'_>,
1564            buf: &[u8],
1565        ) -> Poll<io::Result<usize>> {
1566            let len = buf.len().min(self.max_write);
1567            lock(&self.bytes).extend_from_slice(&buf[..len]);
1568            Poll::Ready(Ok(len))
1569        }
1570
1571        fn poll_write_vectored(
1572            self: Pin<&mut Self>,
1573            _cx: &mut Context<'_>,
1574            bufs: &[IoSlice<'_>],
1575        ) -> Poll<io::Result<usize>> {
1576            let mut remaining = self.max_write;
1577            let mut written = 0;
1578            let mut output = lock(&self.bytes);
1579            for buf in bufs {
1580                let len = buf.len().min(remaining);
1581                output.extend_from_slice(&buf[..len]);
1582                written += len;
1583                remaining -= len;
1584                if remaining == 0 {
1585                    break;
1586                }
1587            }
1588            Poll::Ready(Ok(written))
1589        }
1590
1591        fn poll_flush(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1592            Poll::Ready(Ok(()))
1593        }
1594
1595        fn poll_close(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<io::Result<()>> {
1596            Poll::Ready(Ok(()))
1597        }
1598    }
1599
1600    #[test]
1601    fn small_send_stages_without_a_grant_and_large_send_demands_one() {
1602        let limits = Limits {
1603            max_fragment_size: 8,
1604            trailer_send_copy_threshold: 4,
1605            ..Limits::default()
1606        };
1607
1608        let small_shared = send_shared(limits);
1609        let mut small = TrailerSend::new(small_shared.clone(), ());
1610        let mut cx = Context::from_waker(Waker::noop());
1611        assert!(matches!(
1612            Pin::new(&mut small).poll_write(&mut cx, b"data"),
1613            Poll::Ready(Ok(4))
1614        ));
1615        let header = FragmentHeader {
1616            flags: Flags::NONE,
1617            kind: Kind::Request,
1618            id: 1,
1619            payload_len: 4,
1620        }
1621        .encode();
1622        assert_eq!(
1623            &small_shared.lock().unwrap().buffer[..],
1624            [&header[..], b"data"].concat()
1625        );
1626        assert_eq!(small_shared.lock().unwrap().state, SendState::Fragment);
1627
1628        let large_shared = send_shared(limits);
1629        let mut large = TrailerSend::new(large_shared.clone(), ());
1630        assert!(
1631            Pin::new(&mut large)
1632                .poll_write(&mut cx, b"large")
1633                .is_pending()
1634        );
1635        assert_eq!(large_shared.lock().unwrap().state, SendState::Demand);
1636        assert!(large_shared.lock().unwrap().buffer.is_empty());
1637    }
1638
1639    #[test]
1640    fn receive_copy_threshold_depends_on_demand_for_this_fragment() {
1641        let undemanded = recv_shared(Limits {
1642            trailer_recv_copy_threshold: 1,
1643            trailer_recv_demand_copy_threshold: 4,
1644            ..unbounded_limits()
1645        })
1646        .0;
1647        let (_, receiver) = generic(futures::io::empty(), futures::io::sink());
1648        let mut receiver = AnyReceiver::Generic(receiver);
1649        let lease = unsafe { RecvShared::grant(&undemanded, receiver.recv(), 4) };
1650        assert_eq!(undemanded.lock().unwrap().state, RecvState::Unclaimed);
1651        drop(lease);
1652
1653        let demanded = recv_shared(Limits {
1654            trailer_recv_copy_threshold: 1,
1655            trailer_recv_demand_copy_threshold: 4,
1656            ..unbounded_limits()
1657        })
1658        .0;
1659        let mut trailer = TrailerRecv::new(demanded.clone());
1660        let mut output = [0; 4];
1661        assert!(poll_read_once(&mut trailer, &mut output).is_pending());
1662        assert_eq!(demanded.lock().unwrap().state, RecvState::Demand);
1663        let (_, receiver) = generic(futures::io::empty(), futures::io::sink());
1664        let mut receiver = AnyReceiver::Generic(receiver);
1665        let lease = unsafe { RecvShared::grant(&demanded, receiver.recv(), 4) };
1666        assert_eq!(demanded.lock().unwrap().state, RecvState::Draining);
1667        drop(lease);
1668    }
1669
1670    #[test]
1671    fn demand_at_a_completed_fragment_boundary_applies_to_the_next_fragment() {
1672        let shared = recv_shared(Limits {
1673            trailer_recv_copy_threshold: 0,
1674            trailer_recv_demand_copy_threshold: 0,
1675            ..unbounded_limits()
1676        })
1677        .0;
1678        let mut trailer = TrailerRecv::new(shared.clone());
1679        let (_, receiver) = generic(futures::io::empty(), futures::io::sink());
1680        let mut receiver = AnyReceiver::Generic(receiver);
1681        let lease = unsafe { RecvShared::grant(&shared, receiver.recv(), 0) };
1682        assert_eq!(shared.lock().unwrap().state, RecvState::Fragment);
1683
1684        let mut output = [0; 1];
1685        assert!(poll_read_once(&mut trailer, &mut output).is_pending());
1686        assert_eq!(shared.lock().unwrap().state, RecvState::FragmentDemand);
1687        lease.complete();
1688        assert_eq!(shared.lock().unwrap().state, RecvState::Demand);
1689    }
1690
1691    #[tokio::test]
1692    async fn unclaimed_large_receive_falls_back_to_driver_draining() {
1693        use futures::io::AsyncWriteExt;
1694
1695        let shared = recv_shared(Limits {
1696            trailer_recv_copy_threshold: 0,
1697            trailer_recv_demand_copy_threshold: 0,
1698            ..unbounded_limits()
1699        })
1700        .0;
1701        let (mut writer, reader) = crate::tests::duplex(16);
1702        writer.write_all(b"data").await.unwrap();
1703        let (_, receiver) = generic(reader, futures::io::sink());
1704        let mut receiver = AnyReceiver::Generic(receiver);
1705        let lease = unsafe { RecvShared::grant(&shared, receiver.recv(), 4) };
1706        assert_eq!(shared.lock().unwrap().state, RecvState::Unclaimed);
1707
1708        assert!(!RecvShared::wait_fragment(&shared).await.unwrap());
1709        assert_eq!(shared.lock().unwrap().state, RecvState::Fragment);
1710        assert_eq!(&shared.lock().unwrap().stage[..], b"data");
1711        lease.complete();
1712    }
1713
1714    #[tokio::test]
1715    async fn demanded_large_receive_can_claim_the_grant_directly() {
1716        use futures::io::AsyncWriteExt;
1717
1718        let shared = recv_shared(Limits {
1719            trailer_recv_copy_threshold: 0,
1720            trailer_recv_demand_copy_threshold: 0,
1721            ..unbounded_limits()
1722        })
1723        .0;
1724        let mut trailer = TrailerRecv::new(shared.clone());
1725        let mut output = [0; 4];
1726        assert!(poll_read_once(&mut trailer, &mut output).is_pending());
1727
1728        let (mut writer, reader) = crate::tests::duplex(16);
1729        writer.write_all(b"data").await.unwrap();
1730        let (_, receiver) = generic(reader, futures::io::sink());
1731        let mut receiver = AnyReceiver::Generic(receiver);
1732        let lease = unsafe { RecvShared::grant(&shared, receiver.recv(), 4) };
1733        assert_eq!(shared.lock().unwrap().state, RecvState::Unclaimed);
1734        assert!(matches!(
1735            poll_read_once(&mut trailer, &mut output),
1736            Poll::Ready(Ok(4))
1737        ));
1738        assert_eq!(&output, b"data");
1739        assert!(!RecvShared::wait_fragment(&shared).await.unwrap());
1740        lease.complete();
1741        assert_eq!(shared.lock().unwrap().state, RecvState::Idle);
1742    }
1743
1744    #[tokio::test]
1745    async fn abandoned_fragment_flushes_only_its_real_staged_suffix() {
1746        let output = Arc::new(Mutex::new(Vec::new()));
1747        let (sender, _) = generic(
1748            futures::io::empty(),
1749            CappedSink {
1750                bytes: output.clone(),
1751                max_write: 16,
1752            },
1753        );
1754        let mut sender = AnySender::Generic(sender);
1755        let shared = send_shared(unbounded_limits());
1756        let data = (0..100).map(|value| value as u8).collect::<Vec<_>>();
1757        let mut trailer = TrailerSend::new(shared.clone(), ());
1758        demand(&mut trailer, &data);
1759        let lease = unsafe { SendShared::grant(&shared, sender.send(), 1024) };
1760
1761        let written = std::future::poll_fn(|cx| Pin::new(&mut trailer).poll_write(cx, &data))
1762            .await
1763            .unwrap();
1764        assert_eq!(written, data.len());
1765        {
1766            let inner = lock(&shared);
1767            let header_len = FragmentHeader {
1768                flags: Flags::NONE,
1769                kind: Kind::Request,
1770                id: 1,
1771                payload_len: data.len(),
1772            }
1773            .encode()
1774            .len();
1775            assert_eq!(&inner.buffer[..], &data[16 - header_len..]);
1776        }
1777
1778        drop(trailer);
1779        assert_eq!(
1780            SendShared::wait_fragment(&shared).await.unwrap(),
1781            SendAction::Abort
1782        );
1783        let header_len = FragmentHeader {
1784            flags: Flags::NONE,
1785            kind: Kind::Request,
1786            id: 1,
1787            payload_len: data.len(),
1788        }
1789        .encode()
1790        .len();
1791        assert_eq!(&lock(&output)[header_len..], data);
1792        lease.complete();
1793    }
1794
1795    #[tokio::test]
1796    async fn partial_header_and_payload_share_the_stage_buffer() {
1797        let output = Arc::new(Mutex::new(Vec::new()));
1798        let (sender, _) = generic(
1799            futures::io::empty(),
1800            CappedSink {
1801                bytes: output.clone(),
1802                max_write: 5,
1803            },
1804        );
1805        let mut sender = AnySender::Generic(sender);
1806        let shared = send_shared_id(7, unbounded_limits());
1807        let data = (0..32).map(|value| value as u8).collect::<Vec<_>>();
1808        let mut trailer = TrailerSend::new(shared.clone(), ());
1809        demand(&mut trailer, &data);
1810        let lease = unsafe { SendShared::grant(&shared, sender.send(), 1024) };
1811
1812        let written = std::future::poll_fn(|cx| Pin::new(&mut trailer).poll_write(cx, &data))
1813            .await
1814            .unwrap();
1815        assert_eq!(written, data.len());
1816
1817        let header = FragmentHeader {
1818            flags: Flags::NONE,
1819            kind: Kind::Request,
1820            id: 7,
1821            payload_len: data.len(),
1822        }
1823        .encode();
1824        let mut expected_stage = Vec::from(&header[5..]);
1825        expected_stage.extend_from_slice(&data);
1826        assert_eq!(&lock(&shared).buffer[..], expected_stage);
1827
1828        assert_eq!(
1829            SendShared::wait_fragment(&shared).await.unwrap(),
1830            SendAction::Fragment
1831        );
1832        assert_eq!(&lock(&output)[..], [&header[..], &data].concat());
1833        lease.complete();
1834    }
1835
1836    #[tokio::test]
1837    async fn finish_releases_an_unused_live_grant() {
1838        let (sender, _) = generic(futures::io::empty(), futures::io::sink());
1839        let mut sender = AnySender::Generic(sender);
1840        let shared = send_shared(unbounded_limits());
1841        let lease = unsafe { SendShared::grant(&shared, sender.send(), 1024) };
1842
1843        TrailerSend::new(shared.clone(), ()).finish();
1844        assert_eq!(
1845            SendShared::wait_fragment(&shared).await.unwrap(),
1846            SendAction::Finish
1847        );
1848        lease.complete();
1849    }
1850
1851    /// Exhausting the pool parks the writer instead of failing it. This is
1852    /// the whole behavioural change from the old `max_trailer_size` abort: a
1853    /// slow consumer throttles the sender, it does not break it.
1854    /// A trailer whose message has not been admitted yet must reserve
1855    /// nothing. It cannot send what it stages — a trailer fragment can never
1856    /// precede its own payload — and the credit it took would be held against
1857    /// the trailers that *can* send, whose completion is the only thing that
1858    /// would free the payload quota the unadmitted message is waiting for.
1859    #[tokio::test]
1860    async fn an_unstarted_trailer_reserves_no_credit() {
1861        let session = Arc::new(SessionWindow::new(64));
1862        // Ordinary staging limits, so the write after `start` completes
1863        // against the buffer rather than demanding a transport grant this
1864        // test has no driver to supply.
1865        let shared = SendShared::new(Kind::Request, 1, &Limits::default(), session.clone());
1866        let mut trailer = TrailerSend::new(shared.clone(), ());
1867
1868        let mut cx = Context::from_waker(Waker::noop());
1869        assert!(matches!(
1870            Pin::new(&mut trailer).poll_write(&mut cx, b"abcd"),
1871            Poll::Pending
1872        ));
1873        assert_eq!(session.available(), 64, "nothing may be spent yet");
1874        assert_eq!(lock(&shared).state, SendState::Idle);
1875
1876        SendShared::start(&shared);
1877        let written = std::future::poll_fn(|cx| Pin::new(&mut trailer).poll_write(cx, b"abcd"))
1878            .await
1879            .unwrap();
1880        assert_eq!(written, 4);
1881        assert_eq!(session.available(), 60);
1882    }
1883
1884    #[tokio::test]
1885    async fn exhausted_credit_parks_the_writer_until_credit_arrives() {
1886        let (sender, _) = generic(futures::io::empty(), futures::io::sink());
1887        let mut sender = AnySender::Generic(sender);
1888        let session = Arc::new(SessionWindow::new(4));
1889        let shared = SendShared::new(Kind::Request, 1, &zero_copy_limits(), session.clone());
1890        SendShared::start(&shared);
1891        let mut trailer = TrailerSend::new(shared.clone(), ());
1892
1893        // Spend the whole 4-byte pool.
1894        demand(&mut trailer, b"abcd");
1895        let lease = unsafe { SendShared::grant(&shared, sender.send(), 1024) };
1896        let written = std::future::poll_fn(|cx| Pin::new(&mut trailer).poll_write(cx, b"abcd"))
1897            .await
1898            .unwrap();
1899        assert_eq!(written, 4);
1900        let action = SendShared::wait_fragment(&shared).await.unwrap();
1901        assert_eq!(action, SendAction::Fragment);
1902        lease.complete();
1903
1904        // With nothing left, the next write parks rather than erroring, and
1905        // stays in `Idle` so it holds no transport grant while parked.
1906        let mut cx = Context::from_waker(Waker::noop());
1907        assert!(matches!(
1908            Pin::new(&mut trailer).poll_write(&mut cx, b"e"),
1909            Poll::Pending
1910        ));
1911        assert_eq!(lock(&shared).state, SendState::Idle);
1912        assert!(matches!(
1913            SendShared::poll_action(&shared, &mut cx),
1914            Poll::Pending
1915        ));
1916
1917        // Credit from the peer unblocks exactly that much more.
1918        session.refund(1, 2);
1919        demand(&mut trailer, b"ef");
1920        let lease = unsafe { SendShared::grant(&shared, sender.send(), 1024) };
1921        let written = std::future::poll_fn(|cx| Pin::new(&mut trailer).poll_write(cx, b"ef"))
1922            .await
1923            .unwrap();
1924        assert_eq!(written, 2);
1925        lease.complete();
1926    }
1927
1928    /// A credit pool smaller than a fragment is legal: it just produces
1929    /// short fragments. Only a zero pool would deadlock, and negotiation
1930    /// floors it at 1.
1931    #[tokio::test]
1932    async fn pool_below_fragment_size_still_makes_progress() {
1933        let (sender, _) = generic(futures::io::empty(), futures::io::sink());
1934        let mut sender = AnySender::Generic(sender);
1935        let session = Arc::new(SessionWindow::new(3));
1936        let limits = Limits {
1937            max_fragment_size: 1024,
1938            ..zero_copy_limits()
1939        };
1940        let shared = SendShared::new(Kind::Request, 1, &limits, session.clone());
1941        SendShared::start(&shared);
1942        let mut trailer = TrailerSend::new(shared.clone(), ());
1943        let mut total = 0;
1944        for _ in 0..4 {
1945            demand(&mut trailer, b"abcdefghij");
1946            let lease = unsafe { SendShared::grant(&shared, sender.send(), 1024) };
1947            let n = std::future::poll_fn(|cx| Pin::new(&mut trailer).poll_write(cx, b"abcdefghij"))
1948                .await
1949                .unwrap();
1950            assert_eq!(n, 3, "each write is clamped to the pool, not dropped");
1951            total += n;
1952            SendShared::wait_fragment(&shared).await.unwrap();
1953            lease.complete();
1954            session.refund(1, 3);
1955        }
1956        assert_eq!(total, 12);
1957    }
1958
1959    /// The pool is the only limiter, so one trailer can exhaust it and park
1960    /// another. The parked writer holds no transport grant while it waits,
1961    /// and it is the *other* trailer's consumer retiring bytes that releases
1962    /// it — the cross-trailer coupling that comes with a single pool.
1963    #[tokio::test]
1964    async fn one_trailer_exhausting_the_pool_parks_another() {
1965        let (sender, _) = generic(futures::io::empty(), futures::io::sink());
1966        let mut sender = AnySender::Generic(sender);
1967        let session = Arc::new(SessionWindow::new(4));
1968        let limits = zero_copy_limits();
1969        let first = SendShared::new(Kind::Request, 1, &limits, session.clone());
1970        let second = SendShared::new(Kind::Request, 2, &limits, session.clone());
1971        SendShared::start(&first);
1972        SendShared::start(&second);
1973
1974        let mut trailer = TrailerSend::new(first.clone(), ());
1975        demand(&mut trailer, b"abcdefgh");
1976        let lease = unsafe { SendShared::grant(&first, sender.send(), 1024) };
1977        let written = std::future::poll_fn(|cx| Pin::new(&mut trailer).poll_write(cx, b"abcdefgh"))
1978            .await
1979            .unwrap();
1980        assert_eq!(written, 4, "clamped by the pool");
1981        SendShared::wait_fragment(&first).await.unwrap();
1982        lease.complete();
1983
1984        let mut other = TrailerSend::new(second.clone(), ());
1985        let mut cx = Context::from_waker(Waker::noop());
1986        assert!(matches!(
1987            Pin::new(&mut other).poll_write(&mut cx, b"i"),
1988            Poll::Pending
1989        ));
1990        assert_eq!(
1991            lock(&second).state,
1992            SendState::Idle,
1993            "a parked writer must hold no transport grant"
1994        );
1995        assert!(matches!(
1996            SendShared::poll_action(&second, &mut cx),
1997            Poll::Pending
1998        ));
1999
2000        session.refund(1, 2);
2001        demand(&mut other, b"ij");
2002        let lease = unsafe { SendShared::grant(&second, sender.send(), 1024) };
2003        let written = std::future::poll_fn(|cx| Pin::new(&mut other).poll_write(cx, b"ij"))
2004            .await
2005            .unwrap();
2006        assert_eq!(written, 2);
2007        lease.complete();
2008    }
2009
2010    /// Aborting a trailer must return its share of the pool, or a discarded
2011    /// transfer would shrink the connection's capacity permanently.
2012    #[tokio::test]
2013    async fn aborting_a_trailer_returns_its_session_debt() {
2014        let (sender, _) = generic(futures::io::empty(), futures::io::sink());
2015        let mut sender = AnySender::Generic(sender);
2016        let session = Arc::new(SessionWindow::new(64));
2017        let shared = SendShared::new(Kind::Request, 1, &zero_copy_limits(), session.clone());
2018        SendShared::start(&shared);
2019        let mut trailer = TrailerSend::new(shared.clone(), ());
2020        demand(&mut trailer, b"abcdefgh");
2021        let lease = unsafe { SendShared::grant(&shared, sender.send(), 1024) };
2022        std::future::poll_fn(|cx| Pin::new(&mut trailer).poll_write(cx, b"abcdefgh"))
2023            .await
2024            .unwrap();
2025        SendShared::wait_fragment(&shared).await.unwrap();
2026        lease.complete();
2027        assert_eq!(session.available(), 56);
2028
2029        SendShared::discard(&shared);
2030        assert_eq!(session.available(), 64, "the pool is made whole again");
2031    }
2032
2033    /// Auto-release is the default and needs no participation: bytes are
2034    /// retired as they reach the application.
2035    #[test]
2036    fn auto_release_credits_on_delivery() {
2037        let (shared, sink) = recv_shared(Limits {
2038            trailer_credit_interval: 8,
2039            ..Limits::default()
2040        });
2041        RecvShared::accept_bytes(&shared, 8);
2042        lock(&shared).stage.extend_from_slice(b"abcdefgh");
2043        let mut trailer = TrailerRecv::new(shared.clone());
2044
2045        let mut out = [0u8; 8];
2046        assert!(matches!(
2047            poll_read_once(&mut trailer, &mut out),
2048            Poll::Ready(Ok(8))
2049        ));
2050        assert_eq!(&*lock(&sink.credits), &[(7, 8)]);
2051    }
2052
2053    /// In manual mode reading retires nothing; only `release` does.
2054    #[test]
2055    fn manual_release_does_not_credit_on_read() {
2056        let (shared, sink) = recv_shared(Limits {
2057            trailer_credit_interval: 8,
2058            ..Limits::default()
2059        });
2060        RecvShared::accept_bytes(&shared, 8);
2061        lock(&shared).stage.extend_from_slice(b"abcdefgh");
2062        let mut trailer = TrailerRecv::new(shared.clone());
2063        trailer.set_manual_credit();
2064
2065        let mut out = [0u8; 8];
2066        assert!(matches!(
2067            poll_read_once(&mut trailer, &mut out),
2068            Poll::Ready(Ok(8))
2069        ));
2070        assert!(lock(&sink.credits).is_empty(), "reading must not credit");
2071
2072        trailer.release(8);
2073        assert_eq!(&*lock(&sink.credits), &[(7, 8)]);
2074    }
2075
2076    /// Credit is coalesced at half an interval, so a chunk below that does
2077    /// not put a fragment on the wire by itself.
2078    #[test]
2079    fn credit_is_coalesced_below_half_an_interval() {
2080        let (shared, sink) = recv_shared(Limits {
2081            trailer_credit_interval: 64,
2082            trailer_session_window: 1024,
2083            ..Limits::default()
2084        });
2085        RecvShared::accept_bytes(&shared, 32);
2086        let mut trailer = TrailerRecv::new(shared.clone());
2087        trailer.set_manual_credit();
2088
2089        trailer.release(8);
2090        assert!(lock(&sink.credits).is_empty());
2091        trailer.release(8);
2092        assert!(lock(&sink.credits).is_empty());
2093        // Crossing half the interval (32) flushes the accumulated total as
2094        // one fragment rather than three.
2095        trailer.release(16);
2096        assert_eq!(&*lock(&sink.credits), &[(7, 32)]);
2097    }
2098
2099    /// The anti-deadlock half of the coalescing rule: when the peer has
2100    /// provably run out of credit, even a sub-threshold release must go out,
2101    /// or both ends wait on each other forever.
2102    #[test]
2103    fn credit_is_flushed_when_the_peer_is_starved() {
2104        let (shared, sink) = recv_shared(Limits {
2105            trailer_credit_interval: 64,
2106            trailer_session_window: 64,
2107            ..Limits::default()
2108        });
2109        // The peer has spent the entire pool and is certainly parked.
2110        assert_eq!(RecvShared::accept_bytes(&shared, 64), None);
2111        let mut trailer = TrailerRecv::new(shared.clone());
2112        trailer.set_manual_credit();
2113
2114        trailer.release(1);
2115        assert_eq!(
2116            &*lock(&sink.credits),
2117            &[(7, 1)],
2118            "a starved peer must be credited immediately, however little"
2119        );
2120    }
2121
2122    /// A sub-threshold chunk that accumulated while the consumer was reading
2123    /// must go out the moment the consumer starts waiting, or a sender parked
2124    /// on a per-trailer budget of its own — which this end cannot see — never
2125    /// learns it may continue.
2126    #[test]
2127    fn credit_is_flushed_when_the_consumer_starts_waiting() {
2128        let (shared, sink) = recv_shared(Limits {
2129            trailer_credit_interval: 64,
2130            trailer_session_window: 1024,
2131            ..Limits::default()
2132        });
2133        RecvShared::accept_bytes(&shared, 8);
2134        lock(&shared).stage.extend_from_slice(b"abcdefgh");
2135        let mut trailer = TrailerRecv::new(shared.clone());
2136
2137        let mut out = [0u8; 8];
2138        assert!(matches!(
2139            poll_read_once(&mut trailer, &mut out),
2140            Poll::Ready(Ok(8))
2141        ));
2142        assert!(
2143            lock(&sink.credits).is_empty(),
2144            "8 bytes is below half the interval, so it coalesces while reading"
2145        );
2146
2147        // Nothing staged and nothing granted: the consumer is now blocked on
2148        // the peer, and coalescing has nothing left to gain.
2149        assert!(poll_read_once(&mut trailer, &mut out).is_pending());
2150        assert_eq!(&*lock(&sink.credits), &[(7, 8)]);
2151    }
2152
2153    /// The other edge of the same rule: credit retired *while* the consumer
2154    /// is already waiting — where a manual consumer's `release` lands, since
2155    /// it runs on its own timeline — must not be held back either.
2156    #[test]
2157    fn credit_released_while_waiting_is_flushed_immediately() {
2158        let (shared, sink) = recv_shared(Limits {
2159            trailer_credit_interval: 64,
2160            trailer_session_window: 1024,
2161            ..Limits::default()
2162        });
2163        RecvShared::accept_bytes(&shared, 8);
2164        lock(&shared).stage.extend_from_slice(b"abcdefgh");
2165        let mut trailer = TrailerRecv::new(shared.clone());
2166        trailer.set_manual_credit();
2167
2168        let mut out = [0u8; 8];
2169        assert!(matches!(
2170            poll_read_once(&mut trailer, &mut out),
2171            Poll::Ready(Ok(8))
2172        ));
2173        assert!(poll_read_once(&mut trailer, &mut out).is_pending());
2174        assert!(
2175            lock(&sink.credits).is_empty(),
2176            "manual mode retires nothing on read, so the stall flushes nothing"
2177        );
2178
2179        trailer.release(8);
2180        assert_eq!(
2181            &*lock(&sink.credits),
2182            &[(7, 8)],
2183            "a waiting consumer's release goes out below the threshold"
2184        );
2185    }
2186
2187    /// The starvation flush is session-scoped, and that is what replaces the
2188    /// per-trailer starvation check a two-window scheme would have had: a
2189    /// trailer retiring a sub-threshold chunk must still credit it when the
2190    /// pool was drained by some *other* trailer, or the parked sender never
2191    /// learns there is room.
2192    #[test]
2193    fn credit_is_flushed_when_another_trailer_drained_the_pool() {
2194        let limits = Limits {
2195            trailer_credit_interval: 1024,
2196            trailer_session_window: 64,
2197            ..Limits::default()
2198        };
2199        let session = Arc::new(SessionWindow::new(limits.trailer_session_window));
2200        let make = |id| {
2201            let sink = Arc::new(RecordingSink::default());
2202            let shared = RecvShared::new(
2203                limits.trailer_recv_copy_threshold,
2204                limits.trailer_recv_demand_copy_threshold,
2205                limits.trailer_credit_interval,
2206                session.clone(),
2207                id,
2208                Arc::new(sink.clone()),
2209            );
2210            (shared, sink)
2211        };
2212        let (first, first_sink) = make(1);
2213        let (second, _second_sink) = make(2);
2214
2215        // The other trailer takes all but one byte of the pool, leaving this
2216        // one holding far less than its coalescing interval.
2217        assert_eq!(RecvShared::accept_bytes(&first, 1), None);
2218        assert_eq!(RecvShared::accept_bytes(&second, 63), None);
2219
2220        let mut trailer = TrailerRecv::new(first.clone());
2221        trailer.set_manual_credit();
2222        trailer.release(1);
2223        assert_eq!(
2224            &*lock(&first_sink.credits),
2225            &[(1, 1)],
2226            "a drained pool must flush even a single byte, whoever drained it"
2227        );
2228    }
2229
2230    /// The pool ledger must survive the objects it accounts for: settlement
2231    /// is idempotent and refunds are clamped, so an abort and a `Credit` that
2232    /// crossed it on the wire cannot both pay off the same bytes.
2233    #[test]
2234    fn session_ledger_settles_each_byte_exactly_once() {
2235        let session = SessionWindow::new(100);
2236        assert_eq!(session.debit_up_to(1, 40), 40);
2237        assert_eq!(session.debit_up_to(2, 30), 30);
2238        assert_eq!(session.available(), 30);
2239
2240        // A refund larger than what the id owes is clamped, not overpaid.
2241        session.refund(1, 1000);
2242        assert_eq!(session.available(), 70);
2243        // ... and repeating it adds nothing.
2244        session.refund(1, 1000);
2245        assert_eq!(session.available(), 70);
2246
2247        // Settling returns the remainder once, and only once.
2248        session.settle(2);
2249        assert_eq!(session.available(), 100);
2250        session.settle(2);
2251        session.refund(2, 30);
2252        assert_eq!(session.available(), 100);
2253
2254        // An id the pool never knew about is a no-op, which is what makes a
2255        // credit for an already-finished trailer safe to apply blindly.
2256        session.refund(99, 10);
2257        session.settle(99);
2258        assert_eq!(session.available(), 100);
2259    }
2260
2261    /// `discard` returns the trailer's whole outstanding debt to the pool in
2262    /// one go, so bytes still sitting in `stage` and read afterwards must not
2263    /// be refunded a second time.
2264    #[test]
2265    fn reading_staged_bytes_after_a_discard_does_not_double_refund() {
2266        let limits = Limits {
2267            trailer_credit_interval: 8,
2268            trailer_session_window: 64,
2269            ..Limits::default()
2270        };
2271        let session = Arc::new(SessionWindow::new(limits.trailer_session_window));
2272        let sink = Arc::new(RecordingSink::default());
2273        let shared = RecvShared::new(
2274            limits.trailer_recv_copy_threshold,
2275            limits.trailer_recv_demand_copy_threshold,
2276            limits.trailer_credit_interval,
2277            session.clone(),
2278            7,
2279            Arc::new(sink.clone()),
2280        );
2281
2282        assert!(RecvShared::accept_bytes(&shared, 8).is_none());
2283        assert_eq!(session.available(), 56);
2284        lock(&shared).stage.extend_from_slice(b"abcdefgh");
2285
2286        let mut trailer = TrailerRecv::new(shared.clone());
2287        RecvShared::discard(&shared);
2288        assert_eq!(session.available(), 64, "the debt is returned in full");
2289
2290        // The staged tail is still readable, and must cost the pool nothing.
2291        let mut out = [0u8; 8];
2292        assert!(matches!(
2293            poll_read_once(&mut trailer, &mut out),
2294            Poll::Ready(Ok(8))
2295        ));
2296        assert_eq!(session.available(), 64);
2297        assert!(lock(&sink.credits).is_empty());
2298    }
2299
2300    /// Dropping the handle tells the peer at once, rather than waiting for
2301    /// another unwanted fragment that a parked sender will never send.
2302    #[test]
2303    fn dropping_a_trailer_discards_eagerly_exactly_once() {
2304        let (shared, sink) = recv_shared(unbounded_limits());
2305        let trailer = TrailerRecv::new(shared.clone());
2306        RecvShared::discard(&shared);
2307        assert_eq!(&*lock(&sink.discards), &[7]);
2308        drop(trailer);
2309        assert_eq!(&*lock(&sink.discards), &[7], "idempotent");
2310    }
2311}