Skip to main content

moqtap_proxy/
release_timer.rs

1//! A process-wide release wheel: one dedicated OS thread, one min-heap of
2//! deadlines, one `CancellationToken` per armed deadline.
3//!
4//! `tokio::time::sleep` cannot be used for release timing. Tokio parks its
5//! runtime on a wait with a millisecond timeout, and on Windows every
6//! *interruptible* wait is quantised to the ~15.6 ms system tick: measured
7//! on Windows 11, `tokio::time::sleep(1 ms)` returns after 15.96 ms and
8//! `Condvar::wait_timeout(1 ms)` is 14.6 ms late. `std::thread::sleep` is
9//! the exception — std implements it with a high-resolution waitable timer,
10//! so it lands within ~0.3 ms — but it cannot be woken early.
11//!
12//! This module recovers *accurate *and* interruptible* in plain safe code: the
13//! thread sleeps in [`SLICE`]-bounded steps and re-reads the heap between them,
14//! and parks on an untimed `Condvar` — which *is* woken promptly — when there
15//! is nothing to wait for. End to end (the thread wakes, cancels a token, a
16//! tokio task in a `select!` resumes) the measured lateness on Windows 11 is
17//! p50 0.11-0.17 ms and p95 0.52-0.57 ms, against 11-13 ms for
18//! `tokio::time::sleep`.
19//!
20//! Nothing here is `pub`, the whole module is safe code, and there is no
21//! `#[cfg]` outside its test module: one implementation runs on every
22//! platform. That is what lets this file move into `quinn-netem`, give
23//! each impaired socket its own wheel, or be replaced wholesale, without
24//! any of it being a breaking change.
25
26use std::cmp::{Ordering, Reverse};
27use std::collections::BinaryHeap;
28use std::sync::{Arc, Condvar, Mutex, OnceLock, Weak};
29use std::thread::JoinHandle;
30use std::time::{Duration, Instant};
31use tokio_util::sync::CancellationToken;
32
33use crate::instrument::TimerBackend;
34
35/// How long the release thread may sleep without re-reading the heap.
36///
37/// This is a responsiveness knob, not an accuracy one: it sets how fast a
38/// newly armed *nearer* deadline is noticed, never the accuracy of one the
39/// wheel already holds. The last sleep before a deadline is
40/// `min(remaining, SLICE) == remaining`, so accuracy comes from
41/// `std::thread::sleep` whatever this constant is.
42const SLICE: Duration = Duration::from_micros(500);
43/// Heap length below which the sweep never runs.
44///
45/// Below this many entries the residue is bounded and cheap (~40 bytes a
46/// slot), and rebuilding a short heap on a timer would trade that bounded
47/// residue for an unbounded `retain` rate.
48const SWEEP_MIN_LEN: usize = 64;
49/// Minimum interval between two sweeps.
50const SWEEP_INTERVAL: Duration = Duration::from_millis(250);
51
52/// A registered deadline. Cheap to clone (one `Arc` + one token clone).
53///
54/// Dropping every clone abandons the deadline: the wheel drops the slot at
55/// its own instant, or at the next sweep, whichever comes first. There is
56/// no unregister call.
57#[derive(Clone, Debug)]
58pub(crate) struct Deadline {
59    entry: Arc<Entry>,
60}
61
62impl Deadline {
63    /// The token the wheel cancels once `Instant::now() >= at`.
64    ///
65    /// Level-triggered: awaiting it after the wheel has fired resolves
66    /// immediately, so the engine may create and drop `token().cancelled()`
67    /// on every `select!` iteration without a lost-wakeup race and without
68    /// re-registering with the wheel.
69    pub(crate) fn token(&self) -> &CancellationToken {
70        &self.entry.token
71    }
72
73    /// A deadline that has already passed. Allocates one token, never
74    /// touches the wheel, never starts the release thread.
75    fn already_due() -> Self {
76        let token = CancellationToken::new();
77        token.cancel();
78        Self { entry: Arc::new(Entry { token }) }
79    }
80}
81
82/// Register `at` with the process-wide wheel.
83///
84/// **The sole lazy-initialisation trigger for the release thread.** When
85/// `at <= Instant::now()` it returns an already-cancelled [`Deadline`]
86/// without constructing the wheel, so `Delay { by: 0 }`, a delay swallowed
87/// by the monotonic clamp, and every session that never delays all leave
88/// [`crate::instrument::release_timer_started`] `false`.
89pub(crate) fn arm_at(at: Instant) -> Deadline {
90    if at <= Instant::now() {
91        return Deadline::already_due();
92    }
93    shared().arm(at)
94}
95
96/// The process-wide wheel, constructed on first use. Never dropped: it lives in
97/// a `static OnceLock<ReleaseTimer>`, so its thread is never joined and the
98/// *atexit joins a thread that holds a lock* hazard cannot arise.
99fn shared() -> &'static ReleaseTimer {
100    static SHARED: OnceLock<ReleaseTimer> = OnceLock::new();
101    SHARED.get_or_init(|| {
102        let timer = ReleaseTimer::new();
103        // Published here and not in `ReleaseTimer::new`, which also runs
104        // for owned wheels (this module's tests, any per-socket owner)
105        // that are not the process-wide one.
106        crate::instrument::note_release_timer_started(timer.backend());
107        timer
108    })
109}
110
111/// Whether the process-wide wheel has been constructed.
112///
113/// Read by this module's tests; every non-test caller in the crate wants
114/// [`backend`], which answers the same question and says which one.
115#[allow(dead_code)]
116pub(crate) fn started() -> bool {
117    crate::instrument::release_timer_started()
118}
119
120/// Which primitive the process-wide wheel's thread waits on, or `None` if
121/// it was never constructed. This is what
122/// [`crate::instrument::release_timer_backend`] returns.
123pub(crate) fn backend() -> Option<TimerBackend> {
124    crate::instrument::release_timer_backend()
125}
126
127/// An owned release wheel: one OS thread, one heap, one parker.
128///
129/// The process-wide wheel is one of these in a `OnceLock`. Instances
130/// created directly (this module's tests today, per-socket owners later)
131/// join their thread on drop — unless the drop is itself running on that
132/// thread, which the `Drop` impl below explains.
133pub(crate) struct ReleaseTimer {
134    inner: Arc<Inner>,
135    thread: Option<JoinHandle<()>>,
136}
137
138impl ReleaseTimer {
139    /// Resolve a backend and start the release thread.
140    ///
141    /// Honours `MOQTAP_RELEASE_TIMER` (`slice` | `condvar`), read once,
142    /// here. Unset or unrecognised means `slice`. **Cannot fail**, and
143    /// that is a property of the design rather than an omission: there is
144    /// no OS object to fail to create, so there is no fallback ladder and
145    /// no error path to test.
146    pub(crate) fn new() -> Self {
147        let parker = match std::env::var("MOQTAP_RELEASE_TIMER").as_deref() {
148            Ok("condvar") => Parker::Condvar,
149            // An unrecognised value resolves to the sliced sleeper rather
150            // than panicking: this is a diagnostic knob read inside a
151            // forwarding task, and `backend()` reports what was actually
152            // chosen, so a typo is observable without being fatal.
153            _ => Parker::SleepSlice,
154        };
155        let inner = Arc::new(Inner {
156            state: Mutex::new(State {
157                heap: BinaryHeap::new(),
158                seq: 0,
159                stopping: false,
160                wakes: 0,
161                last_sweep: Instant::now(),
162            }),
163            cv: Condvar::new(),
164            parker,
165        });
166        let thread_inner = Arc::clone(&inner);
167        let thread = std::thread::Builder::new()
168            .name("moqtap-release".to_owned())
169            .spawn(move || run(thread_inner))
170            .expect("release timer thread");
171        Self { inner, thread: Some(thread) }
172    }
173
174    /// Register `at`. `at <= now` yields an already-cancelled [`Deadline`]
175    /// without touching the heap or waking the thread.
176    pub(crate) fn arm(&self, at: Instant) -> Deadline {
177        if at <= Instant::now() {
178            return Deadline::already_due();
179        }
180        let entry = Arc::new(Entry { token: CancellationToken::new() });
181        {
182            let mut g = self.inner.state.lock().expect("release wheel state");
183            g.seq += 1;
184            let seq = g.seq;
185            g.heap.push(Reverse(Slot { at, seq, entry: Arc::downgrade(&entry) }));
186        } // guard released before notify
187        self.inner.cv.notify_one(); // a no-op when nobody waits
188        Deadline { entry }
189    }
190
191    /// Which primitive this wheel's thread waits on.
192    pub(crate) fn backend(&self) -> TimerBackend {
193        match self.inner.parker {
194            Parker::SleepSlice => TimerBackend::SleepSlice,
195            Parker::Condvar => TimerBackend::Condvar,
196        }
197    }
198
199    /// Heap length, including slots whose [`Deadline`] was dropped and
200    /// that have not been popped or swept yet.
201    #[cfg(test)]
202    pub(crate) fn debug_len(&self) -> usize {
203        self.inner.state.lock().expect("release wheel state").heap.len()
204    }
205
206    /// Loop iterations the release thread has completed — one per wake.
207    /// Lets a test assert that an empty wheel costs nothing: the count must
208    /// not move across an idle window.
209    #[cfg(test)]
210    pub(crate) fn debug_wakes(&self) -> u64 {
211        self.inner.state.lock().expect("release wheel state").wakes
212    }
213}
214
215/// Sets `stopping`, wakes the thread, and joins it — except when the drop
216/// is itself running *on* that thread, where it detaches instead. Pending
217/// deadlines are abandoned, not fired: a dropped wheel has no listeners
218/// left.
219///
220/// # Why the join is conditional
221///
222/// The join is cheap only from *another* thread. Measured 0.097 ms with a
223/// 10 s deadline armed, because the thread never waits longer than one
224/// [`SLICE`] and so reads `stopping` promptly — but that measurement was
225/// taken with the wheel dropped from a thread other than its own release
226/// thread, and it licenses nothing about the other case. It was the only
227/// case anything could reach while the never-dropped process-wide wheel
228/// was the sole non-test owner; a wheel owned per socket is droppable
229/// from anywhere.
230///
231/// From the release thread the join is not a slow path, it is a permanent
232/// deadlock. Step 3 of [`run`] cancels tokens *outside* the lock and *on*
233/// the release thread, so every waker registered on a deadline — which is
234/// what awaiting `token().cancelled()` installs — runs there too. A waker
235/// that drops the last handle to the wheel enters this function on the
236/// release thread, and `join()` then waits for the thread executing it.
237/// `stopping` and `notify_all` do not rescue it: the thread is inside the
238/// cancel loop and can never return to the top of `run` to read the flag.
239/// Measured that way, the drop had not returned after 20 s against a
240/// 500 µs [`SLICE`], on every run.
241///
242/// Detaching there is not a compromise, it is the only outcome that
243/// terminates. `stopping` is set and `notify_all` sent before the branch,
244/// so the detached thread finishes the cancel loop it is in, sees the flag
245/// on its next iteration and returns on its own: a bounded, self-clearing
246/// thread rather than an unbounded hang. The handle already carries the
247/// id `spawn` gave the thread, so no separate field is needed to
248/// recognise it.
249impl Drop for ReleaseTimer {
250    fn drop(&mut self) {
251        {
252            let mut g = self.inner.state.lock().expect("release wheel state");
253            g.stopping = true;
254        }
255        self.inner.cv.notify_all();
256        if let Some(handle) = self.thread.take() {
257            if handle.thread().id() == std::thread::current().id() {
258                return; // detach: joining here would join this thread to itself
259            }
260            let _ = handle.join();
261        }
262    }
263}
264
265// ── internals ──────────────────────────────────────────────────────────
266
267/// What a [`Deadline`] and its heap slot share.
268///
269/// The frozen sketch also carried an `at: Instant` here. It is dropped
270/// rather than carried: nothing reads it (the ordering copy lives in
271/// [`Slot`]), and a never-read field is `dead_code`, which this crate
272/// builds with `-D warnings`.
273#[derive(Debug)]
274struct Entry {
275    token: CancellationToken,
276}
277
278/// A heap slot. `Weak`, so a dropped [`Deadline`] costs a skipped pop
279/// rather than a token nobody holds.
280struct Slot {
281    at: Instant,
282    seq: u64,
283    entry: Weak<Entry>,
284}
285
286// `Weak` is neither `Ord` nor `Eq`, so these cannot be derived. Order is
287// `(at, seq)`, `seq` monotonic, so equal instants pop in registration
288// order. The heap is a `BinaryHeap<Reverse<Slot>>`, i.e. a *min*-heap:
289// inverting either half of this pair turns the wheel into a LIFO that
290// serves the farthest deadline first, and nothing but a wrong-looking
291// timing test would say so — which is why
292// `the_heap_is_a_min_heap_and_ties_break_by_registration` below tests it
293// purely.
294impl PartialEq for Slot {
295    fn eq(&self, o: &Self) -> bool {
296        (self.at, self.seq) == (o.at, o.seq)
297    }
298}
299impl Eq for Slot {}
300impl Ord for Slot {
301    fn cmp(&self, o: &Self) -> Ordering {
302        (self.at, self.seq).cmp(&(o.at, o.seq))
303    }
304}
305impl PartialOrd for Slot {
306    fn partial_cmp(&self, o: &Self) -> Option<Ordering> {
307        Some(self.cmp(o))
308    }
309}
310
311/// Everything the release thread and its registrars share under one lock.
312struct State {
313    /// A *min*-heap of armed deadlines.
314    heap: BinaryHeap<Reverse<Slot>>,
315    /// Monotonic registration counter, the tie-break in [`Slot`]'s `Ord`.
316    seq: u64,
317    /// Set by [`ReleaseTimer`]'s `Drop`; the thread returns on its next
318    /// wake.
319    stopping: bool,
320    /// Loop iterations. Always compiled — it is one `u64` increment under
321    /// a lock the thread already holds — and read only by `debug_wakes`.
322    /// The `+=` counts as a read, so `dead_code` does not fire on it in a
323    /// non-test build.
324    wakes: u64,
325    /// When the heap was last rebuilt to drop abandoned slots.
326    last_sweep: Instant,
327}
328
329/// The shared half of a wheel: what the thread and its registrars both
330/// hold an `Arc` to.
331struct Inner {
332    state: Mutex<State>,
333    cv: Condvar,
334    parker: Parker,
335}
336
337/// How the thread waits when a deadline is armed.
338///
339/// **Exactly one implementation ships.** `SleepSlice` is the backend on
340/// every platform. `Condvar` exists because [`TimerBackend::Condvar`] and
341/// `ImpairmentKind::CoarseReleaseTimer` are public API and must be
342/// reachable by a test: forcing it on Windows produces a genuinely
343/// tick-bound wheel — measured p50 lateness 12.256 ms for a 3 ms deadline
344/// — which is exactly what that impairment reports, so the fault
345/// injection is not a simulation. **No `#[cfg]`: both arms compile on
346/// every platform**, and the choice is made once, at construction, from
347/// `MOQTAP_RELEASE_TIMER`. This enum is the seam for adding a platform
348/// timer without touching the wheel.
349enum Parker {
350    /// `std::thread::sleep(min(remaining, SLICE))`, re-reading the heap
351    /// between slices.
352    SleepSlice,
353    /// `Condvar::wait_timeout` for the whole remainder: interruptible
354    /// everywhere, high-resolution only on Unix.
355    Condvar,
356}
357
358/// What the thread should do next. **Pure** — no clock read, no lock — so
359/// `the_wait_plan_never_overshoots` pins the never-overshoot rule without
360/// timing anything.
361#[derive(Debug, PartialEq, Eq)]
362enum Wait {
363    /// The head is due now; fire it before waiting again.
364    Fire,
365    /// Nothing is armed; park on the condvar with no timeout.
366    Idle,
367    /// Sleep this long, then re-read the heap.
368    Sleep(Duration),
369}
370
371/// Decide the next wait from a clock reading and the head of the heap.
372///
373/// Never returns a `Sleep` that runs past `next`: that, plus the
374/// `slot.at <= now` re-check in [`run`], is the whole never-fire-early
375/// guarantee.
376fn plan(now: Instant, next: Option<Instant>, slice: Duration) -> Wait {
377    match next {
378        None => Wait::Idle,
379        Some(at) => match at.checked_duration_since(now) {
380            None => Wait::Fire,                   // overdue
381            Some(d) if d.is_zero() => Wait::Fire, // due exactly now
382            Some(d) => Wait::Sleep(d.min(slice)), // never sleeps past `at`
383        },
384    }
385}
386
387/// The release thread's body.
388fn run(inner: Arc<Inner>) {
389    let mut g = inner.state.lock().expect("release wheel state");
390    loop {
391        g.wakes += 1;
392        if g.stopping {
393            return;
394        }
395        let now = Instant::now();
396
397        // 1. Collect everything due. `s.at <= now` is the whole
398        //    never-fire-early guarantee; nothing else enforces it.
399        let mut fired: Vec<Arc<Entry>> = Vec::new();
400        while g.heap.peek().is_some_and(|Reverse(s)| s.at <= now) {
401            let Reverse(slot) = g.heap.pop().expect("peeked a due slot");
402            if let Some(e) = slot.entry.upgrade() {
403                fired.push(e);
404            }
405        }
406
407        // 2. Sweep abandoned slots. `strong_count()` does not create an
408        //    `Arc`, so nothing can be dropped under the lock here.
409        if g.heap.len() > SWEEP_MIN_LEN
410            && now.saturating_duration_since(g.last_sweep) >= SWEEP_INTERVAL
411        {
412            g.heap.retain(|Reverse(s)| s.entry.strong_count() > 0);
413            g.last_sweep = now;
414        }
415
416        let next = g.heap.peek().map(|Reverse(s)| s.at);
417
418        // 3. Cancel *outside* the lock, then restart the iteration with a
419        //    fresh clock. `cancel()` runs registered wakers; running them
420        //    under the process-global wheel mutex would put scheduler code
421        //    inside it, and a waker that re-armed would deadlock. The
422        //    `Arc<Entry>`s are dropped out here too, for the same reason.
423        if !fired.is_empty() {
424            drop(g);
425            for e in fired {
426                e.token.cancel();
427            }
428            g = inner.state.lock().expect("release wheel state");
429            continue; // `next` is now stale
430        }
431
432        // 4. Wait. The guard was NOT released above, so `next` is current
433        //    and no arm can have slipped in between.
434        match plan(Instant::now(), next, SLICE) {
435            Wait::Fire => continue,
436            Wait::Idle => {
437                g = inner.cv.wait(g).expect("release wheel state");
438            }
439            Wait::Sleep(d) => match inner.parker {
440                // A `notify_one` that lands here is lost on purpose: the
441                // thread is not waiting, and it re-reads the heap within
442                // `d <= SLICE` anyway.
443                Parker::SleepSlice => {
444                    drop(g);
445                    std::thread::sleep(d);
446                    g = inner.state.lock().expect("release wheel state");
447                }
448                // Forced-coarse: interruptible but tick-bound on Windows.
449                // It waits the *whole* remainder rather than a slice —
450                // slicing a `wait_timeout` would cost ~15 ms per slice.
451                Parker::Condvar => {
452                    let rest = next
453                        .expect("Wait::Sleep implies an armed head")
454                        .saturating_duration_since(Instant::now());
455                    g = inner.cv.wait_timeout(g, rest).expect("release wheel state").0;
456                }
457            },
458        }
459    }
460}
461
462#[cfg(test)]
463mod tests {
464    use super::*;
465    use std::future::Future;
466    // Aliased: `Ordering` in this module is `std::cmp::Ordering`, which
467    // `Slot` needs.
468    use std::sync::atomic::{AtomicBool, Ordering as AtomicOrdering};
469    use std::task::{Context, Poll, Wake, Waker};
470
471    // Every test here owns its wheel: it constructs `ReleaseTimer::new()`,
472    // arms against that instance and drops it. None calls `arm_at`,
473    // `shared()` or `started()` in a way that constructs the process-wide
474    // wheel. `cargo test` runs these concurrently with the other modules'
475    // unit tests in one process, so a shared heap would make `debug_len()`
476    // a race — and an owned wheel is torn down through `Drop`, so every run
477    // also exercises the instance API per-socket owners inherit, on both of
478    // that impl's branches.
479
480    /// Nearest-rank percentile of an already-sorted slice.
481    fn pct(sorted: &[Duration], q: usize) -> Duration {
482        assert!(!sorted.is_empty(), "no samples");
483        let rank = (sorted.len() * q).div_ceil(100);
484        sorted[rank.saturating_sub(1).min(sorted.len() - 1)]
485    }
486
487    /// Sort in place and return `(p50, p95, p99, max)`.
488    fn quantiles(samples: &mut [Duration]) -> (Duration, Duration, Duration, Duration) {
489        samples.sort_unstable();
490        (pct(samples, 50), pct(samples, 95), pct(samples, 99), pct(samples, 100))
491    }
492
493    /// The invariant every other release guarantee rests on: a
494    /// wheel that fires early is a wheel that reorders bytes. Structural,
495    /// no timing budget, cannot flake.
496    ///
497    /// Arming one at a time also makes this the coverage of the
498    /// empty-wheel `Condvar::wait` -> `notify_one` path, since the wheel
499    /// is idle before every sample.
500    ///
501    /// *Ablation:* fire the head after any wake instead of testing
502    /// `slot.at <= now` in step 1 of `run`; measured 300 of 300 fired
503    /// early, by up to 4.978 ms.
504    #[tokio::test]
505    async fn a_deadline_never_fires_early() {
506        let timer = ReleaseTimer::new();
507        for i in 0..300u64 {
508            // Deterministic spacings over 0.2-5 ms; 1601 is coprime with
509            // 4801, so 300 samples are 300 distinct durations.
510            let micros = 200 + (i * 1601) % 4801;
511            let at = Instant::now() + Duration::from_micros(micros);
512            let d = timer.arm(at);
513            d.token().cancelled().await;
514            let observed = Instant::now();
515            assert!(
516                observed >= at,
517                "sample {i} ({micros} us) fired {:?} early",
518                at.saturating_duration_since(observed)
519            );
520        }
521    }
522
523    /// Block until the release thread has completed `n` more iterations.
524    ///
525    /// Bounded, and it returns rather than panicking when it gives up: a
526    /// wheel that stops waking is what the callers' own assertions are
527    /// about, and each of them says more about it than this could.
528    fn wait_for_wakes(timer: &ReleaseTimer, n: u64) {
529        let want = timer.debug_wakes() + n;
530        let give_up = Instant::now() + Duration::from_secs(1);
531        while timer.debug_wakes() < want && Instant::now() < give_up {
532            std::thread::sleep(Duration::from_micros(200));
533        }
534    }
535
536    /// The property `SLICE` exists to provide: a nearer deadline
537    /// armed while the thread is already waiting on a far one is noticed
538    /// within one slice.
539    ///
540    /// *Ablation:* sleep the whole `remaining` instead of
541    /// `min(remaining, SLICE)`; measured 1975 ms late, and the far
542    /// deadline fires first. Re-run once the settle became a wake count:
543    /// `the +2 s deadline fired before the +5 ms one`, reached after
544    /// [`wait_for_wakes`] spends its whole bound, because under this
545    /// ablation the second wake it is counting never comes.
546    #[tokio::test]
547    async fn an_earlier_deadline_registered_later_preempts_the_current_wait() {
548        let timer = ReleaseTimer::new();
549        let far = timer.arm(Instant::now() + Duration::from_secs(2));
550        // The near deadline only arrives mid-wait if the thread is already
551        // in its slice loop, and two completed iterations say it is: the
552        // first is the wake that saw `far` armed, and a second can only
553        // follow a slice that ran out. This was a 2 ms sleep, which is the
554        // same claim with nothing behind it.
555        wait_for_wakes(&timer, 2);
556
557        let start = Instant::now();
558        let near_at = start + Duration::from_millis(5);
559        let near =
560            std::thread::scope(|s| s.spawn(|| timer.arm(near_at)).join().expect("arming thread"));
561        near.token().cancelled().await;
562        let elapsed = start.elapsed();
563
564        assert!(!far.token().is_cancelled(), "the +2 s deadline fired before the +5 ms one");
565        assert!(
566            elapsed < Duration::from_millis(500),
567            "the nearer deadline took {elapsed:?} to be noticed"
568        );
569    }
570
571    /// An empty wheel costs nothing: the thread parks on an
572    /// untimed `Condvar::wait`, not on a polling timeout.
573    ///
574    /// The one wake it allows is the post-fire iteration — the `continue`
575    /// in step 3 of `run`, which cancels the fired tokens and then goes
576    /// round once more to find the heap empty and park. So the last token
577    /// can resolve one iteration before the thread is asleep, and it is
578    /// exactly one iteration, because that one finds nothing armed and
579    /// takes the untimed `wait`.
580    ///
581    /// That wake used to be excluded by sleeping 20 ms first, on the
582    /// reasoning that one iteration cannot take longer than that. It
583    /// usually cannot — the probe measured deltas of 0, 0 and then 1
584    /// across three runs of the same binary — but a settle is a guess at
585    /// somebody else's scheduling, and a wrong guess fails a wheel that is
586    /// working. Counting the wake that is known to be coming needs no
587    /// guess, and 1 against the ~13 the ablation produces is not a margin
588    /// worth buying.
589    ///
590    /// *Ablation:* replace the empty-wheel `cv.wait(g)` with
591    /// `cv.wait_timeout(g, SLICE)`; on Windows each such wait returns
592    /// after ~15.6 ms, so the counter gains one per tick over the idle
593    /// window: `the thread woke 14 times over an idle 200ms window; at
594    /// most one is the post-fire iteration parking`.
595    #[tokio::test]
596    async fn the_release_thread_parks_when_the_wheel_is_empty() {
597        let timer = ReleaseTimer::new();
598        let at = Instant::now() + Duration::from_millis(5);
599        let armed: Vec<Deadline> = (0..100).map(|_| timer.arm(at)).collect();
600        for d in &armed {
601            d.token().cancelled().await;
602        }
603
604        /// The window the negative claim is made over. A window is the
605        /// only evidence there is for "nothing happened", so this one is
606        /// a duration and stays one.
607        const IDLE: Duration = Duration::from_millis(200);
608
609        let before = timer.debug_wakes();
610        tokio::time::sleep(IDLE).await;
611        let after = timer.debug_wakes();
612
613        assert!(
614            after - before <= 1,
615            "the thread woke {} times over an idle {IDLE:?} window; at most one is the \
616             post-fire iteration parking",
617            after - before
618        );
619        assert_eq!(timer.debug_len(), 0, "fired slots stayed in the heap");
620    }
621
622    /// Abandoned slots are swept rather than carried to their own
623    /// instants.
624    ///
625    /// The deadlines are far-dated on purpose: an earlier revision armed
626    /// them at +5 ms and asserted an empty heap 300 ms later, at which
627    /// point every slot was 295 ms overdue and the ordinary pop loop
628    /// emptied the heap whether the sweep existed or not — the test could
629    /// not fail. 30 s is also the case the sweep exists for: it is
630    /// `max_hold`'s default, and a torn-down `Hold` is exactly how a dead
631    /// slot survives its own instant.
632    ///
633    /// *Ablation:* disable the `retain` rebuild; measured 10 000 slots
634    /// still present after a second.
635    ///
636    /// This assumes the shipping [`Parker::SleepSlice`]. Under a forced
637    /// `MOQTAP_RELEASE_TIMER=condvar` the thread waits out the *whole*
638    /// remainder, so with only a +30 s head it does not wake to sweep at
639    /// all and this test fails by design — which is one more reason no
640    /// test in this binary may set that variable.
641    #[tokio::test]
642    async fn abandoned_far_deadlines_are_swept() {
643        let timer = ReleaseTimer::new();
644        let far = Instant::now() + Duration::from_secs(30);
645        for _ in 0..10_000 {
646            drop(timer.arm(far));
647        }
648        assert!(timer.debug_len() >= SWEEP_MIN_LEN, "the abandoned slots never reached the heap");
649
650        // Give the thread a reason to wake.
651        let wake = timer.arm(Instant::now() + Duration::from_millis(5));
652        wake.token().cancelled().await;
653
654        let give_up = Instant::now() + Duration::from_secs(1);
655        loop {
656            let len = timer.debug_len();
657            if len < SWEEP_MIN_LEN {
658                break;
659            }
660            assert!(Instant::now() < give_up, "{len} abandoned slots still in the heap after 1 s");
661            tokio::time::sleep(Duration::from_millis(10)).await;
662        }
663    }
664
665    /// The instance API per-socket owners inherit actually stops, rather
666    /// than waiting out its deadlines.
667    ///
668    /// *Ablation:* drop the `stopping` write in `Drop`; the drop had not
669    /// returned after 500 ms and the test hangs.
670    #[test]
671    fn an_instance_timer_joins_its_thread_on_drop() {
672        let timer = ReleaseTimer::new();
673        let held = timer.arm(Instant::now() + Duration::from_secs(10));
674        let t0 = Instant::now();
675        drop(timer);
676        let elapsed = t0.elapsed();
677        drop(held);
678        assert!(
679            elapsed < Duration::from_millis(250),
680            "dropping a wheel with a +10 s deadline took {elapsed:?}"
681        );
682    }
683
684    /// A waker that tears the wheel down when it fires: it owns the only
685    /// [`ReleaseTimer`] handle and drops it from inside `wake`.
686    /// Records where it ran and whether the drop returned, so the test can tell
687    /// "the drop is stuck" from *the waker never fired*.
688    struct DropTheWheelOnWake {
689        wheel: Mutex<Option<ReleaseTimer>>,
690        fired_on: Mutex<Option<String>>,
691        returned: AtomicBool,
692    }
693
694    impl Wake for DropTheWheelOnWake {
695        fn wake(self: Arc<Self>) {
696            self.wake_by_ref();
697        }
698        fn wake_by_ref(self: &Arc<Self>) {
699            *self.fired_on.lock().expect("waker state") =
700                Some(std::thread::current().name().unwrap_or("<unnamed>").to_owned());
701            // The lock guard is a temporary and is released at the end of
702            // this statement, so the wheel is dropped without it held.
703            let wheel = self.wheel.lock().expect("waker state").take();
704            drop(wheel); // the last handle: this runs `ReleaseTimer::drop`
705            self.returned.store(true, AtomicOrdering::SeqCst);
706        }
707    }
708
709    /// Tearing the wheel down from inside a deadline's own completion path
710    /// returns instead of deadlocking.
711    ///
712    /// Step 3 of [`run`] cancels tokens on the release thread, so a waker
713    /// registered on a deadline runs *there*, and a waker that drops the
714    /// last handle to the wheel therefore enters `Drop` on the release
715    /// thread. An unconditional `handle.join()` would then wait for the
716    /// very thread executing it, forever: `stopping` is already set and
717    /// `notify_all` already sent, and neither helps, because the thread is
718    /// inside the cancel loop and cannot reach the top of `run` to read
719    /// the flag.
720    ///
721    /// The waker is an ordinary [`Wake`] implementation registered by
722    /// polling the same `cancelled()` future any awaiting task would, so
723    /// nothing here is a shape only a test produces. Two assertions keep
724    /// it honest: the first poll must return `Pending`, or no waker was
725    /// registered and the drop never ran from a wake at all; and the wake
726    /// must have landed on the release thread, or the self-join was not
727    /// exercised even though the timing passed.
728    ///
729    /// The bound is a liveness limit, not an accuracy bound — a self-join
730    /// never returns at all, so any window catches it and a generous one
731    /// cannot redden because the box was busy. The joining branch is the
732    /// one `an_instance_timer_joins_its_thread_on_drop` covers, and it
733    /// still holds a 250 ms bound.
734    /// *Ablation:* make the join unconditional again. The drop had not returned
735    /// after 20 s against a 500 µs [`SLICE`], so this test spends its whole
736    /// window and fails with *did not return within 10s*.
737    #[test]
738    fn dropping_the_wheel_from_inside_its_own_release_callback_returns() {
739        /// Liveness limit. The fixed drop returns in microseconds; a
740        /// self-joined one never does.
741        const GIVE_UP: Duration = Duration::from_secs(10);
742
743        let timer = ReleaseTimer::new();
744        let deadline = timer.arm(Instant::now() + Duration::from_millis(50));
745        let state = Arc::new(DropTheWheelOnWake {
746            // The waker is now the only owner of the wheel.
747            wheel: Mutex::new(Some(timer)),
748            fired_on: Mutex::new(None),
749            returned: AtomicBool::new(false),
750        });
751
752        let waker = Waker::from(Arc::clone(&state));
753        let mut cx = Context::from_waker(&waker);
754        let mut cancelled = Box::pin(deadline.token().cancelled());
755        assert_eq!(
756            cancelled.as_mut().poll(&mut cx),
757            Poll::Pending,
758            "the deadline resolved on the first poll, so no waker was registered and the \
759             wheel would never be dropped from one",
760        );
761
762        let give_up = Instant::now() + GIVE_UP;
763        while !state.returned.load(AtomicOrdering::SeqCst) {
764            assert!(
765                Instant::now() < give_up,
766                "`ReleaseTimer::drop` did not return within {GIVE_UP:?}: {}",
767                match state.fired_on.lock().expect("waker state").as_deref() {
768                    Some(t) => format!("it is joining thread {t:?} from thread {t:?}"),
769                    None => "the waker never fired at all".to_owned(),
770                },
771            );
772            std::thread::sleep(Duration::from_millis(1));
773        }
774
775        assert_eq!(
776            state.fired_on.lock().expect("waker state").as_deref(),
777            Some("moqtap-release"),
778            "the wake did not run on the release thread, so this test dropped the wheel from \
779             somewhere the join was always safe and proves nothing",
780        );
781    }
782
783    /// The test that makes this module's reason for existing falsifiable.
784    /// Without it, "a dedicated wheel beats `tokio::time::sleep`" — the
785    /// claim the whole file rests on — is never checked at all.
786    ///
787    /// The sampled quantity is `lateness = observed - deadline`: the
788    /// requested 2 ms is subtracted from *both* arms before the percentile
789    /// is taken. On the lateness reading the measured ratio is 31.8x
790    /// (0.351 ms against 11.170 ms), so `x4` leaves 7.9x of headroom; on
791    /// the elapsed reading it would be 1.4x, which is not an assertion.
792    ///
793    /// The assertion is on a *median* because the tokio arm's p50 is
794    /// bounded below by the ~15.6 ms tick as a property of the timer, not
795    /// of the machine, while the wheel arm's p95 does degrade under load.
796    /// If a runner ever deschedules the release thread past 2.79 ms more
797    /// than half the time, weaken the factor to `x2` and record the runner
798    /// here — the ablation still fails at `x2`.
799    ///
800    /// # Why this one runs by default when the other two do not
801    ///
802    /// The two accuracy measurements at the bottom of this module are
803    /// `#[ignore]`d because a saturated box invalidates them, and *this
804    /// test is the same class of claim* — so it was measured rather than
805    /// assumed either way. Under 16 busy-loop processes on 16 logical
806    /// cores: **0 red in 10 runs**. Under 32, i.e. 2x oversubscription:
807    /// **0 red in 23 runs**. It holds where they do not because it samples
808    /// the wheel alone on a four-worker runtime — one arm, one token, one
809    /// wake — while the session-level pairing it resembles measures the
810    /// whole read-hook-queue-release-write pipeline on a current-thread
811    /// runtime that the same load starves. This test must never be
812    /// `#[ignore]`d: it is the one the multi-threaded-runtime requirement
813    /// rests on. If a runner does break it, the remedy is the `x2`
814    /// weakening above, which the ablation still fails — not deletion, and not an
815    /// `#[ignore]`.
816    ///
817    /// *Ablation:* point the wheel arm at a `tokio::time::sleep` task.
818    #[cfg(windows)]
819    #[tokio::test(flavor = "multi_thread", worker_threads = 4)]
820    async fn the_wheel_beats_the_tokio_timer_on_lateness() {
821        let timer = ReleaseTimer::new();
822        let requested = Duration::from_millis(2);
823        let mut wheel: Vec<Duration> = Vec::with_capacity(100);
824        let mut tokio_timer: Vec<Duration> = Vec::with_capacity(100);
825
826        for _ in 0..100 {
827            let at = Instant::now() + requested;
828            let d = timer.arm(at);
829            d.token().cancelled().await;
830            wheel.push(Instant::now().saturating_duration_since(at));
831
832            let at = Instant::now() + requested;
833            tokio::time::sleep(requested).await;
834            tokio_timer.push(Instant::now().saturating_duration_since(at));
835        }
836
837        let (wheel_p50, ..) = quantiles(&mut wheel);
838        let (tokio_p50, ..) = quantiles(&mut tokio_timer);
839        assert!(
840            wheel_p50 * 4 < tokio_p50,
841            "wheel p50 lateness {wheel_p50:?}, tokio p50 lateness {tokio_p50:?}"
842        );
843    }
844
845    /// Pure: no clock read inside the assertions, no thread.
846    ///
847    /// `t0 + 1 ms` is written on the *now* side rather than `t0 - 1 ms` on
848    /// the deadline side because `Instant - Duration` panics near the
849    /// clock's origin.
850    ///
851    /// *Ablation:* change `d.min(slice)` to `slice`; the fourth case
852    /// fails, and it is the case that would otherwise sleep past a
853    /// deadline.
854    #[test]
855    fn the_wait_plan_never_overshoots() {
856        let t0 = Instant::now();
857        assert_eq!(plan(t0, None, SLICE), Wait::Idle);
858        assert_eq!(plan(t0, Some(t0), SLICE), Wait::Fire);
859        assert_eq!(plan(t0 + Duration::from_millis(1), Some(t0), SLICE), Wait::Fire,);
860        // The *nearest deadline is already inside one slice* case, served by
861        // the `min` and not by a branch.
862        assert_eq!(
863            plan(t0, Some(t0 + Duration::from_micros(100)), SLICE),
864            Wait::Sleep(Duration::from_micros(100)),
865        );
866        assert_eq!(plan(t0, Some(t0 + Duration::from_millis(5)), SLICE), Wait::Sleep(SLICE),);
867    }
868
869    /// `Slot`'s `Ord` is hand-written (`Weak` is neither `Ord` nor
870    /// `Eq`) and wrapped in `Reverse`, so one inverted comparison turns
871    /// the wheel into a LIFO that serves the farthest deadline first — a
872    /// defect that would otherwise show up only as a confusing timing
873    /// failure.
874    ///
875    /// *Ablation:* swap the operands in `Ord::cmp`.
876    #[test]
877    fn the_heap_is_a_min_heap_and_ties_break_by_registration() {
878        let t0 = Instant::now();
879        let ms = |n| t0 + Duration::from_millis(n);
880
881        let mut heap: BinaryHeap<Reverse<Slot>> = BinaryHeap::new();
882        for (at, seq) in [(ms(3), 1u64), (ms(1), 2), (ms(2), 3), (ms(1), 4), (ms(1), 5)] {
883            heap.push(Reverse(Slot { at, seq, entry: Weak::new() }));
884        }
885
886        let mut popped: Vec<(Instant, u64)> = Vec::new();
887        while let Some(Reverse(s)) = heap.pop() {
888            popped.push((s.at, s.seq));
889        }
890
891        assert!(
892            popped.windows(2).all(|w| w[0].0 <= w[1].0),
893            "instants did not come out ascending: {popped:?}"
894        );
895        assert_eq!(
896            popped,
897            vec![(ms(1), 2), (ms(1), 4), (ms(1), 5), (ms(2), 3), (ms(3), 1)],
898            "ties did not break by registration order"
899        );
900    }
901
902    /// **Nothing armed is lost.** Every deadline the wheel
903    /// accepts is eventually released, none of them early, and the sweep
904    /// does not eat a live one on its way past.
905    /// This is the completeness half of what the wheel guarantees, and it is
906    /// stated the way it is *because* it has to survive a saturated box —
907    /// unlike the calibration measurements at the bottom of this module. A
908    /// descheduled release thread makes every sample **later**; it cannot make
909    /// one vanish, and it cannot make one early. So the two assertions here are
910    /// "it arrived at all" (with a give-up window 28x the deadline, which is a
911    /// liveness limit and not an accuracy bound) and *it did not arrive before
912    /// its instant*. Neither has a scheduler-dependent number in it.
913    ///
914    /// The shape is chosen so the sweep really runs **while live slots are
915    /// in the heap**, which is the case every other test here misses:
916    /// `a_deadline_never_fires_early` arms one at a time, so the heap never
917    /// reaches [`SWEEP_MIN_LEN`];
918    /// `the_release_thread_parks_when_the_wheel_is_empty`'s hundred slots
919    /// are due 5 ms after construction, long before [`SWEEP_INTERVAL`]
920    /// elapses; and `abandoned_far_deadlines_are_swept`'s ten thousand slots
921    /// are all abandoned, so a sweep that dropped live ones too would look
922    /// identical. Here 128 live and 128 abandoned slots share a heap, the
923    /// deadlines are ~700 ms out so `SWEEP_INTERVAL` passes twice first,
924    /// and the [`Parker::SleepSlice`] thread is awake every [`SLICE`]
925    /// throughout, so the rebuild is guaranteed to execute over a heap
926    /// that is half live.
927    ///
928    /// Arming runs farthest-first, so every later arm is *nearer* than the
929    /// current head and the awaits then run in deadline order.
930    ///
931    /// *Ablation:* in step 2 of [`run`], retain `strong_count() > 1`
932    /// instead of `> 0` — an off-by-one that reads like a tightening,
933    /// since the live `Arc` is held by the [`Deadline`] and not by the
934    /// heap. Measured: this test fails at the first await with all 128
935    /// live deadlines swept out at ~250 ms and never released, while the
936    /// three tests named above all still pass — which is what it is here
937    /// for.
938    #[tokio::test]
939    async fn every_armed_deadline_is_eventually_released() {
940        const LIVE: usize = 128;
941        /// Interleaved with the live ones so a sweep cannot pass over a
942        /// contiguous block of dead slots and miss the live ones.
943        const ABANDONED: usize = 128;
944        /// Far enough out that `SWEEP_INTERVAL` elapses twice before the
945        /// first slot is due.
946        const BASE: Duration = Duration::from_millis(700);
947        /// Spread, so the batch is many wakes rather than one.
948        const STEP: Duration = Duration::from_micros(200);
949        /// A liveness limit, not an accuracy bound: a lost slot never
950        /// arrives at all, so any window catches it and a generous one
951        /// cannot go red because the box was busy.
952        const GIVE_UP: Duration = Duration::from_secs(20);
953
954        let timer = ReleaseTimer::new();
955        let base = Instant::now() + BASE;
956
957        // Farthest first: every subsequent arm is nearer than the head.
958        let mut armed: Vec<(Instant, Deadline)> = Vec::with_capacity(LIVE);
959        for i in 0..LIVE {
960            let at = base + STEP * u32::try_from(LIVE - 1 - i).expect("LIVE fits in u32");
961            armed.push((at, timer.arm(at)));
962            if i < ABANDONED {
963                // Dropped immediately: a slot the wheel must reap, sitting
964                // between two it must not.
965                drop(timer.arm(at + STEP / 2));
966            }
967        }
968        assert!(
969            timer.debug_len() > SWEEP_MIN_LEN,
970            "the heap never reached the length that lets the sweep run, so this test would \
971             pass with or without one: {} slots",
972            timer.debug_len(),
973        );
974
975        // In deadline order, so each await really waits for its own slot.
976        for (i, (at, d)) in armed.iter().rev().enumerate() {
977            tokio::time::timeout(GIVE_UP, d.token().cancelled()).await.unwrap_or_else(|_| {
978                panic!(
979                    "deadline {i} of {LIVE} was armed and never released: {GIVE_UP:?} after a \
980                     deadline {BASE:?} out it is still pending, with {} slot(s) in the heap",
981                    timer.debug_len(),
982                )
983            });
984            let observed = Instant::now();
985            assert!(
986                observed >= *at,
987                "deadline {i} of {LIVE} fired {:?} early",
988                at.saturating_duration_since(observed),
989            );
990        }
991
992        assert_eq!(
993            timer.debug_len(),
994            0,
995            "every slot was popped or swept, so the heap must be empty",
996        );
997    }
998
999    /// Keeps the two process-wide read-backs compiled and exercised.
1000    ///
1001    /// Not a property test like the others here: `started()` and
1002    /// `backend()` are thin readers
1003    /// over `instrument`'s single monotonic atomic, and until `egress.rs`
1004    /// and `session.rs` call them they would otherwise be `dead_code`
1005    /// under `-D warnings`. Asserted in the monotone-safe direction only
1006    /// (`Some` backend implies started), because nothing in this binary
1007    /// constructs the process-wide wheel and a future test that does must
1008    /// not turn this into a race.
1009    #[test]
1010    fn the_process_wide_read_backs_agree() {
1011        if backend().is_some() {
1012            assert!(started(), "a backend was published without `started`");
1013        }
1014    }
1015
1016    // ── calibration, and why it is the one thing here that is opt-in ────
1017    //
1018    // The tests above are *correctness* properties: never early, never
1019    // lost, never reordered, never left parked. Every one of them holds on
1020    // a box whose scheduler is doing something else, because a starved
1021    // release thread can only make a release **later** — and "later" is not
1022    // what any of them assert. (`the_wheel_beats_the_tokio_timer_on_lateness`
1023    // is the one accuracy claim that still runs by default, and its own
1024    // rustdoc gives the loaded measurement that earned it the exception.)
1025    //
1026    // What follows is the other kind of claim: **how late**. That is an
1027    // accuracy measurement, and this is the finding that moved it out of
1028    // `cargo test --workspace`:
1029    //
1030    // > Under real CPU saturation the wheel does not hold its sub-millisecond
1031    // > lateness. It degrades to the same tens of milliseconds
1032    // > `tokio::time::sleep` costs, so *no* formulation of the accuracy
1033    // > claim survives — not an absolute bound, and not a paired
1034    // > wheel-versus-tokio difference either, because both arms degrade
1035    // > together and the separation collapses to zero.
1036    //
1037    // Measured on this Windows 11 box, 16 busy-loop processes on 16 logical
1038    // cores, `release_error_p50_is_within_the_platform_budget` exactly as it
1039    // is written below: **7 red in 10 loaded runs** (p50 5.14 / 5.34 ms
1040    // against its 5 ms bound, p95 14-15 ms, max 63-67 ms), against 0 red in
1041    // 10 idle runs. The paired form was tried and is *worse*: at session
1042    // level the wheel-versus-tokio pairs came out 12.58/12.03, 10.49/11.12,
1043    // 12.58/12.87, 12.58/11.94, 12.58/12.09, 12.58/11.45 and 12.58/13.26 ms
1044    // — the wheel slower than its own control in four of seven — so the
1045    // difference the pairing was supposed to preserve was 0 ns against a
1046    // 3 ms margin.
1047    // So this is not *the bound was too tight*. A p50 that reads 0.1 ms idle
1048    // and 12 ms saturated is not measuring the wheel; it is measuring the box.
1049    // `cargo test --workspace` runs on three shared CI runners with 76 other
1050    // targets beside it, and a gate that reports the runner's load as a code
1051    // defect is a broken gate whichever number is in it.
1052    //
1053    // **This is not concealment, and the two reasons are checkable.**
1054    //
1055    // 1. The capability these tests measure is still gated, by the
1056    //    correctness properties above — which is what an implementation
1057    //    regression actually trips. The `tokio::time::sleep` implementation
1058    //    this module exists to replace is rejected by
1059    //    `an_earlier_deadline_registered_later_preempts_the_current_wait`
1060    //    (a nearer deadline armed mid-wait is noticed within one slice:
1061    //    measured 1975 ms late under the ablation) and by
1062    //    `a_deadline_never_fires_early`, on every platform, with no timing
1063    //    budget in either.
1064    // 2. The measurement this would gate on — schedule 10 000 releases at
1065    //    1 ms, 5 ms, 200 µs spacing and assert p95 release error under a
1066    //    per-platform budget — needs those budgets *derived from the
1067    //    runners*, which is a job for a dedicated quiet-machine run and not
1068    //    for the unit-test gate.
1069    //
1070    // Run it, on an otherwise idle machine:
1071    //
1072    // ```text
1073    // cargo test -p moqtap-proxy --lib -- --ignored --nocapture calibration
1074    // ```
1075    //
1076    // Its session-level companion is
1077    // `actions_timing::calibration_a_five_millisecond_delay_is_measured_as_five_milliseconds`,
1078    // re-homed the same way and for the same reason. Both still assert, and
1079    // both still fail for a release path that regressed — that is the point
1080    // of leaving the numbers in rather than reducing them to `println!`s.
1081    // They are simply not part of the default test run.
1082
1083    /// **Calibration measurement, not a gate. Requires a quiet machine.**
1084    /// Ignored by default; run with `-- --ignored --nocapture`.
1085    ///
1086    /// The standing proxy for a full calibration run, and the first
1087    /// measurement of the Unix backends. 200 deadlines at 3 ms on one
1088    /// instance; `p50 <= 5 ms` when
1089    /// the backend is high-resolution, `<= 60 ms` otherwise.
1090    ///
1091    /// **Why it is opt-in**: see the banner above. Short version — this
1092    /// median is a property of the machine's scheduler at least as much as
1093    /// of the wheel, it went red 7 times in 10 runs under 16 busy-loop
1094    /// processes, and widening the bound past ~10 ms would admit the very
1095    /// `tokio::time::sleep` implementation it exists to reject. There is no
1096    /// number that is both meaningful and load-proof, so the measurement is
1097    /// taken deliberately instead of continuously.
1098    ///
1099    /// Reference numbers, so a future reader has something to compare
1100    /// against. Quiet Windows 11 box (i9-11900K, 16 logical cores),
1101    /// `SleepSlice`, debug profile, this test alone:
1102    ///
1103    /// | | p50 | p95 | p99 | max |
1104    /// |---|---|---|---|---|
1105    /// | idle, 6 consecutive runs | 0.206-0.408 ms | 0.58-0.68 ms | 0.67-0.81 ms | 0.75-1.22 ms |
1106    /// | idle, worst seen in 10 | 5.23 ms | 21.65 ms | 29.00 ms | 36.48 ms |
1107    /// | 16 busy-loop processes | 5.14-5.34 ms | 14.3-15.2 ms | 16-17 ms | 63-67 ms |
1108    ///
1109    /// The equivalent `--release` numbers are p50 0.116-0.168 ms for the
1110    /// same shape; the idle debug numbers above are the honest unoptimised
1111    /// equivalent. Note the idle *worst* row: even with nothing
1112    /// else running this occasionally breaches 5 ms, which is the second
1113    /// reason it cannot be a gate.
1114    ///
1115    /// *Ablation:* point `arm` at a `tokio::time::sleep` task; the
1116    /// high-resolution branch must fail.
1117    #[tokio::test]
1118    #[ignore = "calibration measurement: asserts timing accuracy, which is not \
1119                measurable on a loaded box — run with --ignored on a quiet machine"]
1120    async fn calibration_release_error_p50_is_within_the_platform_budget() {
1121        let timer = ReleaseTimer::new();
1122        let mut lateness: Vec<Duration> = Vec::with_capacity(200);
1123        for _ in 0..200 {
1124            let at = Instant::now() + Duration::from_millis(3);
1125            let d = timer.arm(at);
1126            d.token().cancelled().await;
1127            lateness.push(Instant::now().saturating_duration_since(at));
1128        }
1129        assert_eq!(lateness.len(), 200, "lost samples");
1130
1131        let bound = if timer.backend().is_high_resolution() {
1132            Duration::from_millis(5)
1133        } else {
1134            Duration::from_millis(60)
1135        };
1136        let (p50, p95, p99, max) = quantiles(&mut lateness);
1137        // Printed as well as asserted: a calibration run is worth reading
1138        // even when it is green, and `--nocapture` is how it is invoked.
1139        println!(
1140            "release lateness, backend {}, 200 deadlines at 3 ms: p50 {p50:?}, p95 {p95:?}, \
1141             p99 {p99:?}, max {max:?} (bound {bound:?})",
1142            timer.backend().as_str(),
1143        );
1144        assert!(
1145            p50 <= bound,
1146            "backend {} p50 {p50:?} exceeds {bound:?} (p95 {p95:?}, p99 {p99:?}, max {max:?})",
1147            timer.backend().as_str()
1148        );
1149    }
1150}