Skip to main content

ntex_util/time/
wheel.rs

1//! Hierarchical timer wheel backing ntex timers.
2//!
3//! The design follows the Linux kernel timer wheel (`kernel/time/timer.c`).
4//!
5//! # Clock
6//!
7//! The wheel counts time in *units* of 16 milliseconds (`1 << UNITS`). The
8//! wheel clock `elapsed` is the unit of the last processed expiry, and
9//! `elapsed_time` is the [`Instant`] that corresponds to it. Timer deadlines
10//! are converted to units relative to this pair.
11//!
12//! # Levels
13//!
14//! The wheel has `LVL_DEPTH` (8) levels of `LVL_SIZE` (64) buckets. Each level
15//! is 8 times coarser than the previous one, a timer is placed on the first
16//! level that can represent its delay:
17//!
18//! | Level | Granularity | Range            |
19//! |-------|-------------|------------------|
20//! | 0     | 16 ms       | 0 .. ~1 s        |
21//! | 1     | 128 ms      | ~1 s .. ~8 s     |
22//! | 2     | ~1 s        | ~8 s .. ~64 s    |
23//! | 3     | ~8 s        | ~64 s .. ~8.6 m  |
24//! | 4     | ~65 s       | ~8.6 m .. ~69 m  |
25//! | 5     | ~8.7 m      | ~69 m .. ~9.2 h  |
26//! | 6     | ~70 m       | ~9.2 h .. ~3 d   |
27//! | 7     | ~9.3 h      | ~3 d .. ~24.5 d  |
28//!
29//! Longer delays are clamped to the capacity of the wheel. The expiry is
30//! rounded up to the granularity of the level and the delay is measured from
31//! [`Instant::now()`], so a timer never fires early but may fire up to one
32//! granularity late. Timers are not cascaded to finer
33//! levels, which keeps insertion and removal `O(1)`.
34//!
35//! A bitmap per level tracks occupied buckets, the next expiry is found by
36//! scanning the bitmaps instead of the buckets.
37//!
38//! # Drivers
39//!
40//! Two tasks are spawned lazily on the current thread:
41//!
42//! * [`TimerDriver`] sleeps until the next occupied bucket expires and wakes
43//!   the timers stored in it. The clock advances to the scheduled time of the
44//!   bucket, not to the time the driver woke up, so a late wakeup does not
45//!   delay later timers. Buckets that are overdue are processed at once.
46//!   Wakers are invoked in place while the wheel is held; timer operations
47//!   are not reentrant. Callback timers are queued separately and invoked
48//!   after releasing the wheel.
49//! * [`LowresTimerDriver`] invalidates the cached [`system_time()`] value
50//!   every 150 milliseconds.
51//!
52//! Dropping the timer driver, i.e. when the runtime stops, stops the wheel and
53//! marks all timers as elapsed. Dropping the lowres driver invalidates the
54//! cached system time.
55//!
56//! A runtime does not always drop its pending tasks when it stops, the timer
57//! is also reset once the arbiter storage of the stopped system is cleared,
58//! so that the next runtime on the thread starts its own drivers. Drivers of
59//! an older generation exit without touching the state.
60//!
61//! Registrations retain owned [`ntex_rt::LocalWaker`] handles, specializing
62//! native owner-thread wakes without allocating wrappers or growing entries.
63//! Standard wakers are still accepted at polling boundaries.
64use std::num::NonZeroUsize;
65use std::time::{Duration, Instant, SystemTime};
66use std::{cell::Cell, cmp, future::Future, mem, pin::Pin, rc::Rc, task, task::Poll};
67
68use futures_timer::Delay;
69use slab::Slab;
70
71use super::callbacks::Callbacks;
72use crate::task::LocalWaker;
73
74/// Resolution of the wheel clock, a unit is `1 << UNITS` milliseconds.
75const UNITS: u64 = 4;
76
77/// Each level is `LVL_CLK_DIV` times coarser than the previous one.
78const LVL_CLK_SHIFT: u64 = 3;
79const LVL_CLK_DIV: u64 = 1 << LVL_CLK_SHIFT;
80const LVL_CLK_MASK: u64 = LVL_CLK_DIV - 1;
81
82/// Number of buckets per level.
83const LVL_BITS: u64 = 6;
84const LVL_SIZE: u64 = 1 << LVL_BITS;
85const LVL_MASK: u64 = LVL_SIZE - 1;
86
87/// Number of levels.
88const LVL_DEPTH: u64 = 8;
89
90/// Total number of buckets.
91const WHEEL_SIZE: usize = (LVL_SIZE * LVL_DEPTH) as usize;
92
93/// Delays at or above the cutoff are clamped to `WHEEL_TIMEOUT_MAX`.
94const WHEEL_TIMEOUT_CUTOFF: u64 = lvl_start(LVL_DEPTH);
95const WHEEL_TIMEOUT_MAX: u64 = WHEEL_TIMEOUT_CUTOFF - lvl_gran(LVL_DEPTH - 1);
96
97/// Refresh interval of the cached time.
98const LOWRES_RESOLUTION: Duration = Duration::from_millis(150);
99
100/// Shift of the level clock relative to the wheel clock.
101const fn lvl_shift(lvl: u64) -> u64 {
102    lvl * LVL_CLK_SHIFT
103}
104
105/// Granularity of a level in units.
106const fn lvl_gran(lvl: u64) -> u64 {
107    1 << lvl_shift(lvl)
108}
109
110/// Smallest delay in units that is stored on level `lvl`, `lvl` must be at
111/// least 1.
112const fn lvl_start(lvl: u64) -> u64 {
113    (LVL_SIZE - 1) << ((lvl - 1) * LVL_CLK_SHIFT)
114}
115
116const fn to_units(millis: u64) -> u64 {
117    millis >> UNITS
118}
119
120const fn to_millis(units: u64) -> u64 {
121    units << UNITS
122}
123
124const fn as_millis(dur: Duration) -> u64 {
125    dur.as_secs() * 1_000 + (dur.subsec_millis() as u64)
126}
127
128/// Returns the start of the current runtime turn, see [`ntex_rt::now()`].
129///
130/// Tasks polled in the same turn observe the same instant on neon and compio,
131/// on tokio this is [`Instant::now()`].
132#[inline]
133pub fn now() -> Instant {
134    ntex_rt::now()
135}
136
137/// Returns a cached approximation of the current system time.
138///
139/// The cached value is refreshed at roughly 150 millisecond intervals.
140#[inline]
141pub fn system_time() -> SystemTime {
142    TIMER.with(Timer::system_time)
143}
144
145#[derive(Debug)]
146/// Handle to a timer registered with ntex's local timer wheel.
147///
148/// Dropping the handle cancels the timer. A handle may be reset and reused
149/// after it has elapsed.
150///
151/// Timer operations are not reentrant. Registered wakers must not synchronously
152/// access the timer wheel when cloned, woken or dropped.
153pub struct TimerHandle(NonZeroUsize);
154
155impl TimerHandle {
156    /// Registers a timer that elapses after `millis`.
157    pub fn new(millis: u64) -> Self {
158        TIMER.with(|t| t.add_timer(millis))
159    }
160
161    /// Restarts the timer with a new delay in milliseconds.
162    pub fn reset(&self, millis: u64) {
163        TIMER.with(|t| t.update_timer(self.0.get(), millis));
164    }
165
166    /// Completes the timer immediately and wakes its registered task.
167    pub fn elapse(&self) {
168        TIMER.with(|t| t.remove_timer(self.0.get()));
169    }
170
171    /// Returns `true` if this timer has elapsed.
172    pub fn is_elapsed(&self) -> bool {
173        TIMER.with(|t| t.with_wheel(|w| w.timers[self.0.get()].bucket.is_none()))
174    }
175
176    /// Polls until this timer has elapsed.
177    pub fn poll_elapsed(&self, cx: &mut task::Context<'_>) -> Poll<()> {
178        TIMER.with(|t| {
179            t.with_wheel(|w| {
180                let entry = &mut w.timers[self.0.get()];
181                if entry.bucket.is_none() {
182                    Poll::Ready(())
183                } else {
184                    if entry.task.as_ref().is_none_or(|w| !w.will_wake(cx.waker())) {
185                        entry.task = Some(ntex_rt::LocalWaker::from_waker(cx.waker()));
186                    }
187                    Poll::Pending
188                }
189            })
190        })
191    }
192}
193
194impl Drop for TimerHandle {
195    fn drop(&mut self) {
196        // the wheel is already destroyed if the handle is dropped by
197        // another thread-local destructor
198        let _ = TIMER.try_with(|t| {
199            t.with_wheel(|w| {
200                w.unlink(self.0.get());
201                w.timers.remove(self.0.get());
202            });
203        });
204    }
205}
206
207bitflags::bitflags! {
208    #[derive(Copy, Clone, Debug, Eq, PartialEq)]
209    struct Flags: u8 {
210        /// The timer driver task is spawned.
211        const DRIVER_STARTED = 0b0000_0001;
212        /// The cached time is populated and its refresh sleep is armed.
213        const LOWRES_TIMER   = 0b0000_1000;
214        /// The lowres driver task is spawned.
215        const LOWRES_DRIVER  = 0b0001_0000;
216        /// A timer was scheduled, `system_time()` caches the time.
217        const RUNNING        = 0b0010_0000;
218    }
219}
220
221thread_local! {
222    pub(super) static TIMER: Rc<Timer> = Rc::new(Timer::new());
223}
224
225/// Per-thread timer state, shared by the timer handles and the driver tasks.
226pub(super) struct Timer {
227    /// Wheel clock in units, the expiry that was processed last.
228    elapsed: Cell<u64>,
229    /// Instant that corresponds to `elapsed`, the scheduled time of the last
230    /// processed expiry. Set lazily when the wheel is idle.
231    elapsed_time: Cell<Option<Instant>>,
232    /// Expiry of the earliest occupied bucket, `u64::MAX` if the wheel is empty.
233    next_expiry: Cell<u64>,
234    flags: Cell<Flags>,
235    /// Incremented on reset, drivers of an older generation are stale.
236    pub(super) generation: Cell<u64>,
237    driver: LocalWaker,
238    lowres_stime: Cell<Option<SystemTime>>,
239    lowres_driver: LocalWaker,
240    /// Taken out of the cell for the duration of an operation.
241    wheel: Cell<Option<Box<Wheel>>>,
242}
243
244/// Timer storage.
245pub(super) struct Wheel {
246    /// Timer entries indexed by handle, slot 0 is reserved so handles are
247    /// non-zero.
248    pub(super) timers: Slab<TimerEntry>,
249    /// Handles of the timers stored in each bucket, `lvl * LVL_SIZE + offset`.
250    buckets: Box<[Slab<usize>]>,
251    /// Occupied buckets of each level, bit `n` is set if bucket `n` has timers.
252    occupied: [u64; LVL_DEPTH as usize],
253    /// Allocated only for callbacks, keeping ordinary entries waker-sized.
254    pub(super) callbacks: Option<Box<Callbacks>>,
255}
256
257struct WheelGuard<'a> {
258    slot: &'a Cell<Option<Box<Wheel>>>,
259    wheel: Option<Box<Wheel>>,
260}
261
262impl Drop for WheelGuard<'_> {
263    fn drop(&mut self) {
264        self.slot.set(self.wheel.take());
265    }
266}
267
268#[derive(Debug)]
269pub(super) struct TimerEntry {
270    /// Bucket index, `None` once the timer has elapsed.
271    pub(super) bucket: Option<u16>,
272    /// Key of the entry in the bucket.
273    pub(super) bucket_entry: usize,
274    pub(super) callback: bool,
275    pub(super) task: Option<ntex_rt::LocalWaker>,
276}
277
278#[cfg(target_pointer_width = "64")]
279const _: () = assert!(mem::size_of::<TimerEntry>() == 32);
280
281struct ExpiredTimers<'a> {
282    timers: &'a mut Slab<TimerEntry>,
283    entries: slab::Drain<'a, usize>,
284}
285
286impl Drop for ExpiredTimers<'_> {
287    fn drop(&mut self) {
288        // A panicking wake must not leave entries pointing into a drained bucket.
289        for no in self.entries.by_ref() {
290            self.timers[no].bucket = None;
291        }
292    }
293}
294
295impl Timer {
296    fn new() -> Self {
297        let mut timers = Slab::default();
298        timers.insert(TimerEntry {
299            bucket: None,
300            bucket_entry: 0,
301            callback: false,
302            task: None,
303        });
304
305        Timer {
306            elapsed: Cell::new(0),
307            elapsed_time: Cell::new(None),
308            next_expiry: Cell::new(u64::MAX),
309            flags: Cell::new(Flags::empty()),
310            generation: Cell::new(0),
311            driver: LocalWaker::new(),
312            lowres_stime: Cell::new(None),
313            lowres_driver: LocalWaker::new(),
314            wheel: Cell::new(Some(Box::new(Wheel {
315                timers,
316                buckets: (0..WHEEL_SIZE).map(|_| Slab::new()).collect(),
317                occupied: [0; LVL_DEPTH as usize],
318                callbacks: None,
319            }))),
320        }
321    }
322
323    pub(super) fn with_wheel<F, R>(&self, f: F) -> R
324    where
325        F: FnOnce(&mut Wheel) -> R,
326    {
327        let mut guard = WheelGuard {
328            slot: &self.wheel,
329            wheel: Some(
330                self.wheel
331                    .take()
332                    .expect("timer wheel operations are not reentrant"),
333            ),
334        };
335        f(guard.wheel.as_deref_mut().unwrap())
336    }
337
338    fn insert_flags(&self, flags: Flags) -> Flags {
339        let mut f = self.flags.get();
340        f.insert(flags);
341        self.flags.set(f);
342        f
343    }
344
345    /// Returns the cached system time, the cache is populated only while the
346    /// wheel is running.
347    fn system_time(self: &Rc<Self>) -> SystemTime {
348        if let Some(cur) = self.lowres_stime.get() {
349            cur
350        } else {
351            let now = SystemTime::now();
352            if self.flags.get().contains(Flags::RUNNING) {
353                self.lowres_stime.set(Some(now));
354                self.refresh_lowres();
355            }
356            now
357        }
358    }
359
360    /// Arms the invalidation of the cached time.
361    fn refresh_lowres(self: &Rc<Self>) {
362        if self.flags.get().contains(Flags::LOWRES_DRIVER) {
363            self.lowres_driver.wake();
364        } else {
365            LowresTimerDriver::start(self);
366        }
367    }
368
369    /// Instant that corresponds to the wheel clock.
370    fn elapsed_time(&self) -> Instant {
371        if let Some(elapsed_time) = self.elapsed_time.get() {
372            elapsed_time
373        } else {
374            let elapsed_time = Instant::now();
375            self.elapsed_time.set(Some(elapsed_time));
376            elapsed_time
377        }
378    }
379
380    fn add_timer(self: &Rc<Self>, millis: u64) -> TimerHandle {
381        let no = if millis == 0 {
382            // elapsed immediately
383            self.with_wheel(|w| {
384                w.timers.insert(TimerEntry {
385                    bucket: None,
386                    bucket_entry: 0,
387                    callback: false,
388                    task: None,
389                })
390            })
391        } else {
392            let (idx, expiry) = self.calc_bucket(millis);
393            let no = self.with_wheel(|w| {
394                let no = w.timers.insert(TimerEntry {
395                    bucket: None,
396                    bucket_entry: 0,
397                    callback: false,
398                    task: None,
399                });
400                w.link(no, idx);
401                no
402            });
403            self.update_next_expiry(expiry);
404            no
405        };
406
407        // slot 0 is reserved in `Timer::new()`
408        TimerHandle(NonZeroUsize::new(no).unwrap())
409    }
410
411    /// Moves the timer to a new bucket, a zero delay elapses it and wakes its
412    /// task.
413    fn update_timer(self: &Rc<Self>, hnd: usize, millis: u64) {
414        if millis == 0 {
415            self.remove_timer(hnd);
416        } else {
417            let (idx, expiry) = self.calc_bucket(millis);
418            self.with_wheel(|w| w.relink(hnd, idx));
419            self.update_next_expiry(expiry);
420        }
421    }
422
423    /// Elapses the timer and wakes its task.
424    fn remove_timer(&self, hnd: usize) {
425        self.with_wheel(|w| {
426            if w.unlink(hnd)
427                && let Some(task) = w.timers[hnd].task.take()
428            {
429                task.wake();
430            }
431        });
432    }
433
434    /// Returns the bucket index and the bucket expiry for a delay starting now.
435    pub(super) fn calc_bucket(self: &Rc<Self>, millis: u64) -> (usize, u64) {
436        self.insert_flags(Flags::RUNNING);
437
438        // The delay is measured from the wheel clock. The cached time is not
439        // used, it goes stale while the thread is blocked and the timer would
440        // fire early by its age
441        let since = Instant::now().saturating_duration_since(self.elapsed_time());
442        let delta = to_units(as_millis(since) + millis);
443        self.calc_wheel_index(self.elapsed.get().wrapping_add(delta), delta)
444    }
445
446    /// Selects the level for a timer that expires at `expires`, `delta` units
447    /// from now.
448    fn calc_wheel_index(&self, expires: u64, delta: u64) -> (usize, u64) {
449        for lvl in 0..LVL_DEPTH {
450            if delta < lvl_start(lvl + 1) {
451                return Self::calc_index(expires, lvl);
452            }
453        }
454        // expire larger delays at the capacity limit of the wheel
455        Self::calc_index(
456            self.elapsed.get().wrapping_add(WHEEL_TIMEOUT_MAX),
457            LVL_DEPTH - 1,
458        )
459    }
460
461    /// Returns the bucket index and the bucket expiry on level `lvl`.
462    fn calc_index(expires: u64, lvl: u64) -> (usize, u64) {
463        // The timer must not fire early. Early expiry can happen because the
464        // timer is armed at the edge of a tick, or because the expiry is
465        // truncated to the level granularity, round up to prevent it.
466        let expires = (expires + lvl_gran(lvl)) >> lvl_shift(lvl);
467        (
468            (lvl * LVL_SIZE + (expires & LVL_MASK)) as usize,
469            expires << lvl_shift(lvl),
470        )
471    }
472
473    /// Wakes the driver if the new bucket expires before the current deadline.
474    pub(super) fn update_next_expiry(self: &Rc<Self>, expiry: u64) {
475        if expiry < self.next_expiry.get() {
476            self.next_expiry.set(expiry);
477            if self.flags.get().contains(Flags::DRIVER_STARTED) {
478                self.driver.wake();
479            } else {
480                TimerDriver::start(self);
481            }
482        }
483    }
484
485    /// Instant at which the bucket expiring at `expiry` is due.
486    fn expiry_time(&self, expiry: u64) -> Instant {
487        self.elapsed_time()
488            + Duration::from_millis(to_millis(expiry.saturating_sub(self.elapsed.get())))
489    }
490
491    /// Returns the expiry of the earliest occupied bucket.
492    fn next_pending_bucket(&self, wheel: &Wheel) -> Option<u64> {
493        let mut clk = self.elapsed.get();
494        let mut next = u64::MAX;
495
496        for lvl in 0..LVL_DEPTH {
497            let lvl_clk = clk & LVL_CLK_MASK;
498            let occupied = wheel.occupied[lvl as usize];
499
500            if occupied != 0 {
501                // distance to the next occupied bucket, wrapping around the level
502                let pos = u64::from(
503                    occupied
504                        .rotate_right((clk & LVL_MASK) as u32)
505                        .trailing_zeros(),
506                );
507                next = cmp::min(next, (clk + pos) << lvl_shift(lvl));
508
509                // The next level is reached once the clock of this level wraps
510                // to a multiple of `LVL_CLK_DIV`, an earlier bucket here cannot
511                // be preceded by one of the next level.
512                if pos <= ((LVL_CLK_DIV - lvl_clk) & LVL_CLK_MASK) {
513                    break;
514                }
515            }
516
517            // Clock of the next level. A partially elapsed tick of this level
518            // is rounded up, as the next level bucket it belongs to was already
519            // processed.
520            clk >>= LVL_CLK_SHIFT;
521            clk += u64::from(lvl_clk != 0);
522        }
523
524        if next < u64::MAX { Some(next) } else { None }
525    }
526
527    /// Removes `flags`, and `RUNNING` so the cached time is not populated and
528    /// no driver is spawned after the runtime stopped.
529    fn remove_flags(&self, flags: Flags) {
530        let mut f = self.flags.get();
531        f.remove(flags | Flags::RUNNING);
532        self.flags.set(f);
533    }
534
535    /// Invalidates the cached time, called when the lowres driver is dropped.
536    fn stop_lowres(&self) {
537        self.remove_flags(Flags::LOWRES_DRIVER | Flags::LOWRES_TIMER);
538        self.lowres_stime.set(None);
539    }
540
541    /// Stops the drivers of the current generation, called when the system
542    /// that runs them has stopped.
543    fn reset(&self) {
544        self.generation.set(self.generation.get().wrapping_add(1));
545        self.stop_wheel();
546        self.stop_lowres();
547        self.driver.take_local();
548        self.lowres_driver.take_local();
549    }
550
551    /// Marks all timers as elapsed and resets the wheel, called when the timer
552    /// driver is dropped. Tasks are not woken, the runtime is stopping.
553    fn stop_wheel(&self) {
554        self.remove_flags(Flags::DRIVER_STARTED);
555
556        self.with_wheel(|wheel| {
557            let Wheel {
558                timers,
559                buckets,
560                occupied,
561                callbacks,
562            } = wheel;
563            for bucket in buckets.iter_mut() {
564                for no in bucket.drain() {
565                    timers[no].bucket = None;
566                }
567            }
568            *occupied = [0; LVL_DEPTH as usize];
569            if let Some(callbacks) = callbacks {
570                callbacks.ready.clear();
571                for ticket in callbacks.entries.values() {
572                    if let Some(state) = ticket.state.upgrade() {
573                        state.pending.set(false);
574                    }
575                }
576            }
577
578            self.next_expiry.set(u64::MAX);
579            self.elapsed.set(0);
580            self.elapsed_time.set(None);
581        });
582    }
583}
584
585impl Wheel {
586    /// Level and occupied bit of a bucket.
587    fn bucket_bit(idx: usize) -> (usize, u64) {
588        (idx / LVL_SIZE as usize, 1 << (idx % LVL_SIZE as usize))
589    }
590
591    /// Stores the timer in bucket `idx`.
592    pub(super) fn link(&mut self, hnd: usize, idx: usize) {
593        let entry = &mut self.timers[hnd];
594        entry.bucket = Some(idx as u16);
595        entry.bucket_entry = self.buckets[idx].insert(hnd);
596
597        let (lvl, bit) = Self::bucket_bit(idx);
598        self.occupied[lvl] |= bit;
599    }
600
601    /// Removes the timer from its bucket and marks it as elapsed.
602    ///
603    /// Returns `false` if the timer has already elapsed.
604    pub(super) fn unlink(&mut self, hnd: usize) -> bool {
605        let entry = &mut self.timers[hnd];
606        if let Some(idx) = entry.bucket.take() {
607            let idx = idx as usize;
608            let bucket = &mut self.buckets[idx];
609            bucket.remove(entry.bucket_entry);
610            if bucket.is_empty() {
611                let (lvl, bit) = Self::bucket_bit(idx);
612                self.occupied[lvl] &= !bit;
613            }
614            true
615        } else {
616            false
617        }
618    }
619
620    /// Moves the timer to bucket `idx`.
621    pub(super) fn relink(&mut self, hnd: usize, idx: usize) {
622        if self.timers[hnd].bucket != Some(idx as u16) {
623            self.unlink(hnd);
624            self.link(hnd, idx);
625        }
626    }
627
628    /// Marks timers elapsed, waking tasks in place and queueing only callbacks.
629    fn execute_expired_timers(&mut self, mut clk: u64) {
630        for lvl in 0..LVL_DEPTH {
631            let idx = ((clk & LVL_MASK) + lvl * LVL_SIZE) as usize;
632            let bucket = &mut self.buckets[idx];
633            if !bucket.is_empty() {
634                let (lvl, bit) = Self::bucket_bit(idx);
635                self.occupied[lvl] &= !bit;
636                let mut expired = ExpiredTimers {
637                    timers: &mut self.timers,
638                    entries: bucket.drain(),
639                };
640                for no in expired.entries.by_ref() {
641                    let entry = &mut expired.timers[no];
642                    entry.bucket = None;
643                    if entry.callback {
644                        let callbacks = self.callbacks.as_mut().unwrap();
645                        callbacks.ready.push(callbacks.entries[&no].clone());
646                    } else if let Some(task) = entry.task.take() {
647                        task.wake();
648                    }
649                }
650            }
651
652            // The next level expires only when the clock is a multiple of its
653            // granularity
654            if (clk & LVL_CLK_MASK) != 0 {
655                break;
656            }
657            clk >>= LVL_CLK_SHIFT;
658        }
659    }
660}
661
662/// Resets the timer when dropped with the arbiter storage of a stopped system.
663#[derive(Default)]
664struct TimerReset;
665
666impl TimerReset {
667    fn register() {
668        ntex_rt::with_item::<TimerReset, _, _>(|_| ());
669    }
670}
671
672impl Drop for TimerReset {
673    fn drop(&mut self) {
674        // the timer is already destroyed if the thread is exiting
675        let _ = TIMER.try_with(|t| t.reset());
676    }
677}
678
679/// Task that sleeps until the next bucket expires and wakes its timers.
680struct TimerDriver {
681    timer: Rc<Timer>,
682    generation: u64,
683    sleep: Delay,
684    /// Deadline the sleep is armed for.
685    armed: Option<Instant>,
686}
687
688impl TimerDriver {
689    fn start(timer: &Rc<Timer>) {
690        timer.insert_flags(Flags::DRIVER_STARTED);
691        TimerReset::register();
692
693        let deadline = timer.expiry_time(timer.next_expiry.get());
694        crate::spawn_detached(TimerDriver {
695            timer: timer.clone(),
696            generation: timer.generation.get(),
697            sleep: Delay::new(deadline.saturating_duration_since(Instant::now())),
698            armed: Some(deadline),
699        });
700    }
701}
702
703impl Drop for TimerDriver {
704    fn drop(&mut self) {
705        if self.timer.generation.get() == self.generation {
706            self.timer.stop_wheel();
707        }
708    }
709}
710
711impl Future for TimerDriver {
712    type Output = ();
713
714    fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
715        let this = &mut *self;
716        let timer = &this.timer;
717        if timer.generation.get() != this.generation {
718            return Poll::Ready(());
719        }
720        timer.driver.register(cx.waker());
721
722        let mut now = Instant::now();
723
724        loop {
725            let expiry = timer.next_expiry.get();
726            if expiry == u64::MAX {
727                // the wheel is empty, a new timer wakes the driver
728                return Poll::Pending;
729            }
730
731            let deadline = timer.expiry_time(expiry);
732            if deadline > now {
733                if this.armed != Some(deadline) {
734                    this.armed = Some(deadline);
735                    this.sleep.reset(deadline.saturating_duration_since(now));
736                }
737                if Pin::new(&mut this.sleep).poll(cx).is_pending() {
738                    return Poll::Pending;
739                }
740                if deadline > now {
741                    // the sleep fired before the deadline, re-arm it
742                    this.armed = None;
743                    continue;
744                }
745            }
746
747            // Advance the clock to the scheduled time of the bucket, a late
748            // wakeup must not shift the timers that expire later
749            timer.elapsed.set(expiry);
750            timer.elapsed_time.set(Some(deadline));
751
752            let (next, callbacks) = timer.with_wheel(|w| {
753                w.execute_expired_timers(expiry);
754                let callbacks = w.callbacks.as_mut().and_then(|callbacks| {
755                    if callbacks.ready.is_empty() {
756                        None
757                    } else {
758                        Some(mem::take(&mut callbacks.ready))
759                    }
760                });
761                (timer.next_pending_bucket(w), callbacks)
762            });
763
764            if let Some(next) = next {
765                timer.next_expiry.set(next);
766            } else {
767                timer.next_expiry.set(u64::MAX);
768                timer.elapsed_time.set(None);
769            }
770
771            if let Some(mut callbacks) = callbacks {
772                for ticket in callbacks.drain(..) {
773                    if timer.generation.get() != this.generation {
774                        return Poll::Ready(());
775                    }
776                    if ticket.generation == this.generation
777                        && let Some(state) = ticket.state.upgrade()
778                        && state.arm.get() == ticket.arm
779                        && state.pending.replace(false)
780                    {
781                        (state.callback)();
782                    }
783                }
784                // A callback or its captures may have stopped this runtime.
785                if timer.generation.get() != this.generation {
786                    return Poll::Ready(());
787                }
788                timer.with_wheel(|wheel| wheel.callbacks.as_mut().unwrap().ready = callbacks);
789                now = Instant::now();
790            }
791        }
792    }
793}
794
795/// Task that invalidates the cached time every `LOWRES_RESOLUTION`.
796struct LowresTimerDriver {
797    timer: Rc<Timer>,
798    generation: u64,
799    sleep: Delay,
800}
801
802impl LowresTimerDriver {
803    fn start(timer: &Rc<Timer>) {
804        timer.insert_flags(Flags::LOWRES_DRIVER);
805        TimerReset::register();
806
807        crate::spawn_detached(LowresTimerDriver {
808            timer: timer.clone(),
809            generation: timer.generation.get(),
810            sleep: Delay::new(LOWRES_RESOLUTION),
811        });
812    }
813}
814
815impl Drop for LowresTimerDriver {
816    fn drop(&mut self) {
817        if self.timer.generation.get() == self.generation {
818            self.timer.stop_lowres();
819        }
820    }
821}
822
823impl Future for LowresTimerDriver {
824    type Output = ();
825
826    fn poll(mut self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
827        let this = &mut *self;
828        let timer = &this.timer;
829        if timer.generation.get() != this.generation {
830            return Poll::Ready(());
831        }
832        timer.lowres_driver.register(cx.waker());
833
834        // the cache was populated, invalidate it after `LOWRES_RESOLUTION`
835        let mut flags = timer.flags.get();
836        if !flags.contains(Flags::LOWRES_TIMER) {
837            flags.insert(Flags::LOWRES_TIMER);
838            timer.flags.set(flags);
839            this.sleep.reset(LOWRES_RESOLUTION);
840        }
841
842        if Pin::new(&mut this.sleep).poll(cx).is_ready() {
843            timer.lowres_stime.set(None);
844            flags.remove(Flags::LOWRES_TIMER);
845            timer.flags.set(flags);
846        }
847        Poll::Pending
848    }
849}
850
851#[cfg(test)]
852mod tests {
853    use std::cell::RefCell;
854    use std::panic::{AssertUnwindSafe, catch_unwind};
855    use std::sync::Arc;
856    use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
857    use std::task::{Context, RawWaker, RawWakerVTable, Wake, Waker};
858
859    use super::*;
860    use crate::future::OnDropFn;
861    use crate::time::{CallbackTimer, Millis, interval, sleep};
862
863    #[derive(Default)]
864    struct InPlaceWaker {
865        wakes: AtomicUsize,
866        delay: Duration,
867        panic_on_wake: bool,
868    }
869
870    impl Wake for InPlaceWaker {
871        fn wake(self: Arc<Self>) {
872            TIMER.with(|timer| {
873                let wheel = timer.wheel.take();
874                let held = wheel.is_none();
875                timer.wheel.set(wheel);
876                assert!(held, "waker must run in place while the wheel is held");
877            });
878            let previous = self.wakes.fetch_add(1, Ordering::Relaxed);
879            assert!(!self.panic_on_wake, "notification");
880            if previous == 0 && !self.delay.is_zero() {
881                std::thread::sleep(self.delay);
882            }
883        }
884    }
885
886    fn timer_state() -> Rc<Timer> {
887        TIMER.with(|timer| {
888            timer.reset();
889            // These tests drive expiry manually, without an executor.
890            timer.insert_flags(Flags::DRIVER_STARTED);
891            timer.clone()
892        })
893    }
894
895    /// STATEMENT: Callback timer diagnostics expose the handle without borrowing the wheel or invoking callbacks.
896    ///
897    /// HOW:
898    /// - Format an armed timer while the wheel is borrowed, then cancel and format it again.
899    /// - Check the same handle is reported and the captured callback has not run.
900    #[test]
901    fn debug_callback_timer() {
902        let timer = timer_state();
903        let calls = Rc::new(Cell::new(0));
904        let captured = calls.clone();
905        let callback = CallbackTimer::new(Millis(10_000), move || {
906            captured.set(captured.get() + 1);
907        });
908        let expected = format!("CallbackTimer {{ handle: {}, .. }}", callback.hnd);
909        timer.with_wheel(|_| assert_eq!(format!("{callback:?}"), expected));
910        assert!(!callback.is_elapsed());
911        callback.cancel();
912        assert_eq!(format!("{callback:?}"), expected);
913        assert!(callback.is_elapsed());
914        assert_eq!(calls.get(), 0);
915    }
916
917    fn timer_driver(timer: &Rc<Timer>) -> TimerDriver {
918        TimerDriver {
919            timer: timer.clone(),
920            generation: timer.generation.get(),
921            sleep: Delay::new(Duration::ZERO),
922            armed: None,
923        }
924    }
925
926    fn make_due(timer: &Timer) {
927        timer.elapsed_time.set(Some(
928            Instant::now().checked_sub(Duration::from_secs(1)).unwrap(),
929        ));
930    }
931
932    #[derive(Debug)]
933    struct NoopNotify;
934
935    impl ntex_rt::Notify for NoopNotify {
936        fn notify(&self) -> std::io::Result<()> {
937            Ok(())
938        }
939    }
940
941    /// STATEMENT: Timer waker registration avoids redundant clones and survives replacement panics.
942    ///
943    /// HOW:
944    /// - Count raw-waker clones and references while re-registering, resetting, and replacing.
945    /// - Catch a clone panic, then verify expiry wakes only the replacement and drop releases it.
946    #[test]
947    fn test_local_waker_registration() {
948        struct Registration {
949            inner: Waker,
950            clones: AtomicUsize,
951            panic_on_clone: AtomicBool,
952        }
953
954        // Keep clone identity stable without relying on generic vtable deduplication.
955        static VTABLE: RawWakerVTable = RawWakerVTable::new(
956            |data| unsafe {
957                let state = &*data.cast::<Registration>();
958                assert!(!state.panic_on_clone.load(Ordering::Relaxed), "clone");
959                state.clones.fetch_add(1, Ordering::Relaxed);
960                Arc::increment_strong_count(data.cast::<Registration>());
961                RawWaker::new(data, &VTABLE)
962            },
963            |data| unsafe {
964                Arc::from_raw(data.cast::<Registration>())
965                    .inner
966                    .wake_by_ref();
967            },
968            |data| unsafe {
969                (*data.cast::<Registration>()).inner.wake_by_ref();
970            },
971            |data| unsafe {
972                drop(Arc::from_raw(data.cast::<Registration>()));
973            },
974        );
975
976        let wrap = |task: &Arc<InPlaceWaker>| {
977            let state = Arc::new(Registration {
978                inner: Waker::from(task.clone()),
979                clones: AtomicUsize::new(0),
980                panic_on_clone: AtomicBool::new(false),
981            });
982            let waker = unsafe {
983                Waker::from_raw(RawWaker::new(Arc::into_raw(state.clone()).cast(), &VTABLE))
984            };
985            (state, waker)
986        };
987
988        let timer = timer_state();
989        let handle = TimerHandle::new(10_000);
990        let old_task = Arc::new(InPlaceWaker::default());
991        let new_task = Arc::new(InPlaceWaker::default());
992        let (old, old_waker) = wrap(&old_task);
993        let (new, new_waker) = wrap(&new_task);
994        let mut old_cx = Context::from_waker(&old_waker);
995        let mut new_cx = Context::from_waker(&new_waker);
996
997        assert!(handle.poll_elapsed(&mut old_cx).is_pending());
998        handle.reset(20_000);
999        assert!(handle.poll_elapsed(&mut old_cx).is_pending());
1000        assert_eq!(old.clones.load(Ordering::Relaxed), 1);
1001        assert_eq!(Arc::strong_count(&old), 3);
1002
1003        new.panic_on_clone.store(true, Ordering::Relaxed);
1004        assert!(catch_unwind(AssertUnwindSafe(|| handle.poll_elapsed(&mut new_cx))).is_err());
1005        timer.with_wheel(|wheel| {
1006            assert!(
1007                wheel.timers[handle.0.get()]
1008                    .task
1009                    .as_ref()
1010                    .unwrap()
1011                    .will_wake(&old_waker)
1012            );
1013        });
1014        assert_eq!(Arc::strong_count(&old), 3);
1015        assert_eq!(Arc::strong_count(&new), 2);
1016
1017        new.panic_on_clone.store(false, Ordering::Relaxed);
1018        assert!(handle.poll_elapsed(&mut new_cx).is_pending());
1019        assert!(handle.poll_elapsed(&mut new_cx).is_pending());
1020        assert_eq!(new.clones.load(Ordering::Relaxed), 1);
1021        assert_eq!(Arc::strong_count(&old), 2);
1022        assert_eq!(Arc::strong_count(&new), 3);
1023
1024        timer.with_wheel(|wheel| {
1025            wheel.relink(handle.0.get(), 3);
1026            wheel.execute_expired_timers(3);
1027        });
1028        assert!(handle.poll_elapsed(&mut new_cx).is_ready());
1029        assert_eq!(new.clones.load(Ordering::Relaxed), 1);
1030        assert_eq!(new_task.wakes.load(Ordering::Relaxed), 1);
1031        assert_eq!(old_task.wakes.load(Ordering::Relaxed), 0);
1032        assert_eq!(Arc::strong_count(&new), 2);
1033
1034        handle.reset(10_000);
1035        assert!(handle.poll_elapsed(&mut old_cx).is_pending());
1036        handle.reset(0);
1037        assert!(handle.is_elapsed());
1038        assert_eq!(old_task.wakes.load(Ordering::Relaxed), 1);
1039        assert_eq!(Arc::strong_count(&old), 2);
1040
1041        handle.reset(10_000);
1042        assert!(handle.poll_elapsed(&mut old_cx).is_pending());
1043        drop(handle);
1044        assert_eq!(old_task.wakes.load(Ordering::Relaxed), 1);
1045        assert_eq!(old.clones.load(Ordering::Relaxed), 3);
1046        assert_eq!(Arc::strong_count(&old), 2);
1047    }
1048
1049    /// STATEMENT: A timer wakes the most recently registered native task and supports later reuse.
1050    ///
1051    /// HOW:
1052    /// - Register two runtime tasks on one handle and expire it, checking only the second finishes.
1053    /// - Rearm for the first task, expire again, and wake its saved waker after runtime shutdown.
1054    #[test]
1055    fn test_local_waker_native_replacement() {
1056        let timer = timer_state();
1057        let rt = ntex_rt::Runtime::new(Box::new(NoopNotify));
1058        let handle = Rc::new(TimerHandle::new(10_000));
1059        let first_waker = Rc::new(Cell::new(None));
1060        let (first_handle, saved) = (handle.clone(), first_waker.clone());
1061        let first = rt.spawn(std::future::poll_fn(move |cx| {
1062            saved.set(Some(cx.waker().clone()));
1063            first_handle.poll_elapsed(cx)
1064        }));
1065        rt.poll();
1066
1067        let second_handle = handle.clone();
1068        let second = rt.spawn(std::future::poll_fn(move |cx| {
1069            second_handle.poll_elapsed(cx)
1070        }));
1071        rt.poll();
1072        timer.with_wheel(|wheel| {
1073            wheel.relink(handle.0.get(), 3);
1074            wheel.execute_expired_timers(3);
1075        });
1076        rt.poll();
1077        assert!(!first.is_finished());
1078        assert!(second.is_finished());
1079
1080        handle.reset(10_000);
1081        first_waker.take().unwrap().wake();
1082        rt.poll();
1083        assert!(!first.is_finished());
1084        handle.reset(0);
1085        rt.poll();
1086        assert!(first.is_finished());
1087        assert_eq!(Rc::strong_count(&handle), 1);
1088        drop(rt);
1089        first_waker.take().unwrap().wake();
1090    }
1091
1092    /// STATEMENT: Timer handles safely retain stale task wakers across runtime shutdown and reuse.
1093    ///
1094    /// HOW:
1095    /// - Register timer and driver wakers, drop the runtime, and reset timer state.
1096    /// - Check released task references and driver slots, then optionally reuse the handle in a new runtime.
1097    #[test]
1098    fn test_local_waker_runtime_shutdown() {
1099        for reuse in [false, true] {
1100            let timer = timer_state();
1101            let rt = ntex_rt::Runtime::new(Box::new(NoopNotify));
1102            let handle = Rc::new(TimerHandle::new(10_000));
1103            let (task_timer, task_handle) = (timer.clone(), handle.clone());
1104            let task = rt.spawn(std::future::poll_fn(move |cx| {
1105                task_timer.driver.register(cx.waker());
1106                task_timer.lowres_driver.register(cx.waker());
1107                task_handle.poll_elapsed(cx)
1108            }));
1109            rt.poll();
1110            drop(rt);
1111            assert!(task.is_finished());
1112            assert_eq!(Rc::strong_count(&handle), 1);
1113            drop(task);
1114
1115            timer.reset();
1116            assert!(handle.is_elapsed());
1117            assert!(!timer.driver.is_set());
1118            assert!(!timer.lowres_driver.is_set());
1119            timer.with_wheel(|wheel| {
1120                assert!(wheel.timers[handle.0.get()].task.is_some());
1121            });
1122
1123            if reuse {
1124                timer.insert_flags(Flags::DRIVER_STARTED);
1125                handle.reset(10_000);
1126                let rt = ntex_rt::Runtime::new(Box::new(NoopNotify));
1127                let task_handle = handle.clone();
1128                let task = rt.spawn(std::future::poll_fn(move |cx| task_handle.poll_elapsed(cx)));
1129                rt.poll();
1130                assert!(!task.is_finished());
1131                handle.elapse();
1132                rt.poll();
1133                assert!(task.is_finished());
1134                assert_eq!(Rc::strong_count(&handle), 1);
1135            }
1136            drop(handle);
1137            timer.with_wheel(|wheel| assert_eq!(wheel.timers.len(), 1));
1138        }
1139    }
1140
1141    /// STATEMENT: Timer wakers can be replaced across successive systems on the same thread.
1142    ///
1143    /// HOW:
1144    /// - Reuse one handle across three systems with alternating native and wrapped wakers.
1145    /// - Reset and expire it in each system and check wrapped notification counts after shutdown.
1146    #[test]
1147    fn test_local_waker_in_next_system() {
1148        struct Wrapped {
1149            inner: Waker,
1150            wakes: Arc<AtomicUsize>,
1151        }
1152
1153        impl Wake for Wrapped {
1154            fn wake(self: Arc<Self>) {
1155                self.wakes.fetch_add(1, Ordering::Relaxed);
1156                self.inner.wake_by_ref();
1157            }
1158        }
1159
1160        std::thread::spawn(|| {
1161            let mut handle = None::<TimerHandle>;
1162            for wrapped in [false, true, false] {
1163                let notifications = Arc::new(AtomicUsize::new(0));
1164                let count = notifications.clone();
1165                handle = Some(
1166                    ntex::rt::System::build()
1167                        .build(ntex::rt::DefaultRuntime)
1168                        .block_on(async move {
1169                            let handle = if let Some(handle) = handle {
1170                                assert!(handle.is_elapsed());
1171                                handle.reset(10_000);
1172                                handle
1173                            } else {
1174                                TimerHandle::new(10_000)
1175                            };
1176                            std::future::poll_fn(|cx| {
1177                                let waker = if wrapped {
1178                                    Waker::from(Arc::new(Wrapped {
1179                                        inner: cx.waker().clone(),
1180                                        wakes: count.clone(),
1181                                    }))
1182                                } else {
1183                                    cx.waker().clone()
1184                                };
1185                                let cx = &mut Context::from_waker(&waker);
1186                                assert!(handle.poll_elapsed(cx).is_pending());
1187                                handle.reset(0);
1188                                assert!(handle.poll_elapsed(cx).is_ready());
1189                                handle.reset(10_000);
1190                                assert!(handle.poll_elapsed(cx).is_pending());
1191                                Poll::Ready(())
1192                            })
1193                            .await;
1194                            handle
1195                        }),
1196                );
1197                assert!(handle.as_ref().unwrap().is_elapsed());
1198                assert_eq!(notifications.load(Ordering::Relaxed), usize::from(wrapped));
1199            }
1200        })
1201        .join()
1202        .unwrap();
1203    }
1204
1205    /// STATEMENT: Callback timers can be canceled and repeatedly rearmed for one delivery each.
1206    ///
1207    /// HOW:
1208    /// - Cancel a reset timer and verify no callback runs after waiting.
1209    /// - Rearm with zero delay twice and check elapsed state and one additional call per expiry.
1210    #[ntex::test]
1211    async fn test_callback_timer() {
1212        let calls = Rc::new(Cell::new(0));
1213        let count = calls.clone();
1214        let callback = crate::time::CallbackTimer::new(Millis::ZERO, move || {
1215            count.set(count.get() + 1);
1216        });
1217        assert!(!callback.is_elapsed());
1218        assert_eq!(calls.get(), 0);
1219        callback.reset(Millis(10_000));
1220        callback.cancel();
1221        assert!(callback.is_elapsed());
1222        sleep(Millis(50)).await;
1223        assert_eq!(calls.get(), 0);
1224
1225        for expected in 1..=2 {
1226            callback.reset(Millis::ZERO);
1227            assert!(!callback.is_elapsed());
1228            sleep(Millis(50)).await;
1229            assert!(callback.is_elapsed());
1230            assert_eq!(calls.get(), expected);
1231        }
1232    }
1233
1234    /// STATEMENT: Stale callback tickets cannot invoke canceled, reset, dropped, or reused entries.
1235    ///
1236    /// HOW:
1237    /// - Queue expired callbacks, then cancel, reset, and drop them before driver delivery.
1238    /// - Reuse the freed slot, verify captures were released, and check only new deadlines fire once.
1239    #[test]
1240    fn test_callback_stale_tickets() {
1241        let timer = timer_state();
1242        let calls = Rc::new(Cell::new(0));
1243        let count = calls.clone();
1244        let cancelled = CallbackTimer::new(Millis(1), move || count.set(count.get() + 1));
1245        let count = calls.clone();
1246        let reset = CallbackTimer::new(Millis(1), move || count.set(count.get() + 1));
1247        let capture = Rc::new(Cell::new(0));
1248        let weak = Rc::downgrade(&capture);
1249        let dropped = CallbackTimer::new(Millis(1), move || capture.set(1));
1250        let old_slot = dropped.hnd;
1251        timer.with_wheel(|wheel| {
1252            for handle in [&cancelled, &reset, &dropped] {
1253                wheel.relink(handle.hnd.get(), 3);
1254            }
1255            wheel.execute_expired_timers(3);
1256        });
1257        assert!(cancelled.is_elapsed());
1258        assert!(reset.is_elapsed());
1259        assert!(dropped.is_elapsed());
1260        assert_eq!(calls.get(), 0);
1261        cancelled.cancel();
1262        reset.reset(Millis(10_000));
1263        drop(dropped);
1264        assert!(
1265            weak.upgrade().is_none(),
1266            "queued tickets must not retain captures"
1267        );
1268        let count = calls.clone();
1269        let replacement = CallbackTimer::new(Millis(10_000), move || count.set(count.get() + 100));
1270        assert_eq!(replacement.hnd, old_slot);
1271
1272        make_due(&timer);
1273        let mut driver = timer_driver(&timer);
1274        assert!(
1275            Pin::new(&mut driver)
1276                .poll(&mut Context::from_waker(Waker::noop()))
1277                .is_pending()
1278        );
1279        assert_eq!(calls.get(), 0);
1280        assert!(!reset.is_elapsed());
1281        assert!(!replacement.is_elapsed());
1282        timer.elapsed_time.set(Some(
1283            Instant::now().checked_sub(Duration::from_secs(20)).unwrap(),
1284        ));
1285        for _ in 0..2 {
1286            assert!(
1287                Pin::new(&mut driver)
1288                    .poll(&mut Context::from_waker(Waker::noop()))
1289                    .is_pending()
1290            );
1291            assert_eq!(calls.get(), 101);
1292        }
1293    }
1294
1295    /// STATEMENT: A callback can rearm itself and cancel another callback already due for delivery.
1296    ///
1297    /// HOW:
1298    /// - Expire two callbacks that each cancel their peer and reset themselves.
1299    /// - Verify only one runs initially and its rearmed deadline produces one further call.
1300    #[test]
1301    fn test_callback_self_rearm_and_cross_cancel() {
1302        let timer = timer_state();
1303        let calls = Rc::new(Cell::new(0));
1304        let handles = Rc::new(RefCell::new(Vec::<CallbackTimer>::new()));
1305        for index in 0..2 {
1306            let count = calls.clone();
1307            let weak = Rc::downgrade(&handles);
1308            let callback = CallbackTimer::new(Millis(1), move || {
1309                count.set(count.get() + 1);
1310                let handles = weak.upgrade().unwrap();
1311                let handles = handles.borrow();
1312                assert!(handles[index].is_elapsed());
1313                handles[1 - index].cancel();
1314                handles[index].reset(Millis(10_000));
1315            });
1316            handles.borrow_mut().push(callback);
1317        }
1318        timer.with_wheel(|wheel| {
1319            for handle in handles.borrow().iter() {
1320                wheel.relink(handle.hnd.get(), 1);
1321            }
1322        });
1323        make_due(&timer);
1324        let mut driver = timer_driver(&timer);
1325        assert!(
1326            Pin::new(&mut driver)
1327                .poll(&mut Context::from_waker(Waker::noop()))
1328                .is_pending()
1329        );
1330        assert_eq!(calls.get(), 1);
1331        assert_eq!(
1332            handles.borrow().iter().filter(|h| !h.is_elapsed()).count(),
1333            1
1334        );
1335        timer.elapsed_time.set(Some(
1336            Instant::now().checked_sub(Duration::from_secs(20)).unwrap(),
1337        ));
1338        for _ in 0..2 {
1339            assert!(
1340                Pin::new(&mut driver)
1341                    .poll(&mut Context::from_waker(Waker::noop()))
1342                    .is_pending()
1343            );
1344            assert_eq!(calls.get(), 2);
1345        }
1346    }
1347
1348    /// STATEMENT: Dropping callback captures can safely create, reset, and cancel timers.
1349    ///
1350    /// HOW:
1351    /// - Drop queued and actively executing callbacks whose capture destructors manipulate timers.
1352    /// - Check deferred capture release, slot reuse, cancellation, and exactly-once later delivery.
1353    #[test]
1354    fn test_callback_capture_drop_schedules_timers() {
1355        for active in [false, true] {
1356            let timer = timer_state();
1357            let released = Rc::new(Cell::new(false));
1358            let finished = Rc::new(Cell::new(false));
1359            let reset_calls = Rc::new(Cell::new(0));
1360            let new_calls = Rc::new(Cell::new(0));
1361            let count = reset_calls.clone();
1362            let reset = Rc::new(CallbackTimer::new(Millis(20_000), move || {
1363                count.set(count.get() + 1);
1364            }));
1365            let cancelled = Rc::new(CallbackTimer::new(Millis(20_000), || {
1366                panic!("cancelled callback");
1367            }));
1368            let replacement = Rc::new(RefCell::new(None::<CallbackTimer>));
1369            let capture = {
1370                let reset = reset.clone();
1371                let cancelled = cancelled.clone();
1372                let replacement = replacement.clone();
1373                let new_calls = new_calls.clone();
1374                let released = released.clone();
1375                let finished = finished.clone();
1376                OnDropFn::new(move || {
1377                    assert!(!active || finished.get());
1378                    let ordinary = TimerHandle::new(10_000);
1379                    ordinary.reset(0);
1380                    drop(ordinary);
1381                    cancelled.cancel();
1382                    reset.reset(Millis(10_000));
1383                    *replacement.borrow_mut() =
1384                        Some(CallbackTimer::new(Millis(10_000), move || {
1385                            new_calls.set(new_calls.get() + 1);
1386                        }));
1387                    released.set(true);
1388                })
1389            };
1390            let handle = Rc::new(RefCell::new(None::<CallbackTimer>));
1391            let weak = Rc::downgrade(&handle);
1392            let was_released = released.clone();
1393            *handle.borrow_mut() = Some(CallbackTimer::new(Millis(1), move || {
1394                let _capture = &capture;
1395                assert!(active, "dropped queued callback must not run");
1396                drop(weak.upgrade().unwrap().borrow_mut().take());
1397                assert!(!was_released.get());
1398                finished.set(true);
1399            }));
1400            let old_slot = handle.borrow().as_ref().unwrap().hnd;
1401            if !active {
1402                timer.with_wheel(|wheel| {
1403                    wheel.relink(old_slot.get(), 1);
1404                    wheel.execute_expired_timers(1);
1405                });
1406                drop(handle.borrow_mut().take());
1407                assert!(released.get());
1408            }
1409            make_due(&timer);
1410            let mut driver = timer_driver(&timer);
1411            assert!(
1412                Pin::new(&mut driver)
1413                    .poll(&mut Context::from_waker(Waker::noop()))
1414                    .is_pending()
1415            );
1416            assert!(released.get());
1417            assert!(handle.borrow().is_none());
1418            assert!(cancelled.is_elapsed());
1419            assert!(!reset.is_elapsed());
1420            assert_eq!(replacement.borrow().as_ref().unwrap().hnd, old_slot);
1421            assert!(!replacement.borrow().as_ref().unwrap().is_elapsed());
1422            assert_eq!((reset_calls.get(), new_calls.get()), (0, 0));
1423
1424            timer.elapsed_time.set(Some(
1425                Instant::now().checked_sub(Duration::from_secs(20)).unwrap(),
1426            ));
1427            for _ in 0..2 {
1428                assert!(
1429                    Pin::new(&mut driver)
1430                        .poll(&mut Context::from_waker(Waker::noop()))
1431                        .is_pending()
1432                );
1433                assert_eq!((reset_calls.get(), new_calls.get()), (1, 1));
1434            }
1435        }
1436    }
1437
1438    /// STATEMENT: A callback capture can reset the runtime without the old driver consuming new work.
1439    ///
1440    /// HOW:
1441    /// - Drop a callback during delivery and reset timer state from its capture destructor.
1442    /// - Queue a replacement, verify the old driver exits, and deliver it once with a new driver.
1443    #[test]
1444    fn test_callback_capture_drop_resets_runtime() {
1445        let timer = timer_state();
1446        let new_calls = Rc::new(Cell::new(0));
1447        let replacement = Rc::new(RefCell::new(None::<CallbackTimer>));
1448        let capture = {
1449            let timer = timer.clone();
1450            let new_calls = new_calls.clone();
1451            let replacement = replacement.clone();
1452            OnDropFn::new(move || {
1453                timer.reset();
1454                timer.insert_flags(Flags::DRIVER_STARTED);
1455                let callback = CallbackTimer::new(Millis(1), move || {
1456                    new_calls.set(new_calls.get() + 1);
1457                });
1458                timer.with_wheel(|wheel| {
1459                    wheel.relink(callback.hnd.get(), 1);
1460                    wheel.execute_expired_timers(1);
1461                });
1462                *replacement.borrow_mut() = Some(callback);
1463            })
1464        };
1465        let handle = Rc::new(RefCell::new(None::<CallbackTimer>));
1466        let weak = Rc::downgrade(&handle);
1467        *handle.borrow_mut() = Some(CallbackTimer::new(Millis(1), move || {
1468            let _capture = &capture;
1469            drop(weak.upgrade().unwrap().borrow_mut().take());
1470        }));
1471        make_due(&timer);
1472        let mut old_driver = timer_driver(&timer);
1473        assert!(
1474            Pin::new(&mut old_driver)
1475                .poll(&mut Context::from_waker(Waker::noop()))
1476                .is_ready()
1477        );
1478        assert!(handle.borrow().is_none());
1479        assert!(replacement.borrow().as_ref().unwrap().is_elapsed());
1480        assert_eq!(new_calls.get(), 0);
1481        drop(old_driver);
1482
1483        make_due(&timer);
1484        let mut driver = timer_driver(&timer);
1485        for _ in 0..2 {
1486            assert!(
1487                Pin::new(&mut driver)
1488                    .poll(&mut Context::from_waker(Waker::noop()))
1489                    .is_pending()
1490            );
1491            assert_eq!(new_calls.get(), 1);
1492        }
1493    }
1494
1495    /// STATEMENT: Callback delivery refreshes time after a slow callback.
1496    ///
1497    /// HOW:
1498    /// - Schedule two deadlines and make the first callback block past the second.
1499    /// - Poll the driver once and verify both callbacks ran and both timers elapsed.
1500    #[test]
1501    fn test_callback_refreshes_time() {
1502        let timer = timer_state();
1503        let calls = Rc::new(Cell::new(0));
1504        let handles = [1, 32].map(|millis| {
1505            let calls = calls.clone();
1506            CallbackTimer::new(Millis(millis), move || {
1507                let previous = calls.replace(calls.get() + 1);
1508                if previous == 0 {
1509                    std::thread::sleep(Duration::from_millis(64));
1510                }
1511            })
1512        });
1513        timer.with_wheel(|wheel| {
1514            wheel.relink(handles[0].hnd.get(), 1);
1515            wheel.relink(handles[1].hnd.get(), 3);
1516        });
1517        timer.elapsed_time.set(Some(
1518            Instant::now()
1519                .checked_sub(Duration::from_millis(32))
1520                .unwrap(),
1521        ));
1522        let mut driver = timer_driver(&timer);
1523        assert!(
1524            Pin::new(&mut driver)
1525                .poll(&mut Context::from_waker(Waker::noop()))
1526                .is_pending()
1527        );
1528        assert_eq!(calls.get(), 2);
1529        assert!(handles.iter().all(CallbackTimer::is_elapsed));
1530    }
1531
1532    /// STATEMENT: Dropping a panicked driver cleans callbacks rearmed before the panic.
1533    ///
1534    /// HOW:
1535    /// - Run a callback that rearms itself and panics while other timers remain pending.
1536    /// - Drop the driver and verify elapsed handles, cleared callback flags, and empty wheel queues.
1537    #[test]
1538    fn test_callback_panic_after_rearm_cleanup() {
1539        let timer = timer_state();
1540        let callback = Rc::new_cyclic(|weak: &std::rc::Weak<CallbackTimer>| {
1541            let weak = weak.clone();
1542            CallbackTimer::new(Millis(1), move || {
1543                let callback = weak.upgrade().unwrap();
1544                callback.reset(Millis(10_000));
1545                panic!("callback");
1546            })
1547        });
1548        let later = CallbackTimer::new(Millis(10_000), || panic!("not due"));
1549        let ordinary = TimerHandle::new(10_000);
1550        make_due(&timer);
1551        let mut driver = timer_driver(&timer);
1552        assert!(
1553            catch_unwind(AssertUnwindSafe(|| {
1554                let _ = Pin::new(&mut driver).poll(&mut Context::from_waker(Waker::noop()));
1555            }))
1556            .is_err()
1557        );
1558        assert!(!callback.is_elapsed());
1559        drop(driver);
1560        assert!(callback.is_elapsed());
1561        assert!(!callback.state.pending.get());
1562        assert!(later.is_elapsed());
1563        assert!(ordinary.is_elapsed());
1564        assert!(!later.state.pending.get());
1565        timer.with_wheel(|wheel| {
1566            assert!(wheel.buckets.iter().all(Slab::is_empty));
1567            assert_eq!(wheel.occupied, [0; LVL_DEPTH as usize]);
1568            assert!(wheel.callbacks.as_ref().unwrap().ready.is_empty());
1569        });
1570    }
1571
1572    /// STATEMENT: Resetting timer state during callback delivery stops the old generation safely.
1573    ///
1574    /// HOW:
1575    /// - Queue two callbacks that reset runtime state and create a replacement.
1576    /// - Verify only one old callback runs and the replacement survives old-driver drop for later delivery.
1577    #[test]
1578    fn test_callback_runtime_reset_during_delivery() {
1579        let timer = timer_state();
1580        let calls = Rc::new(Cell::new(0));
1581        let new_calls = Rc::new(Cell::new(0));
1582        let replacement = Rc::new(RefCell::new(None::<CallbackTimer>));
1583        let handles = [0, 1].map(|_| {
1584            let timer = timer.clone();
1585            let calls = calls.clone();
1586            let new_calls = new_calls.clone();
1587            let replacement = replacement.clone();
1588            CallbackTimer::new(Millis(1), move || {
1589                calls.set(calls.get() + 1);
1590                timer.reset();
1591                timer.insert_flags(Flags::DRIVER_STARTED);
1592                let new_calls = new_calls.clone();
1593                *replacement.borrow_mut() = Some(CallbackTimer::new(Millis(1), move || {
1594                    new_calls.set(new_calls.get() + 1);
1595                }));
1596            })
1597        });
1598        timer.with_wheel(|wheel| {
1599            for handle in &handles {
1600                wheel.relink(handle.hnd.get(), 1);
1601            }
1602        });
1603        make_due(&timer);
1604        let mut old_driver = timer_driver(&timer);
1605        assert!(
1606            Pin::new(&mut old_driver)
1607                .poll(&mut Context::from_waker(Waker::noop()))
1608                .is_ready()
1609        );
1610        assert_eq!(calls.get(), 1);
1611        assert!(handles.iter().all(CallbackTimer::is_elapsed));
1612        drop(old_driver);
1613        assert!(!replacement.borrow().as_ref().unwrap().is_elapsed());
1614        make_due(&timer);
1615        let mut driver = timer_driver(&timer);
1616        assert!(
1617            Pin::new(&mut driver)
1618                .poll(&mut Context::from_waker(Waker::noop()))
1619                .is_pending()
1620        );
1621        assert_eq!(new_calls.get(), 1);
1622    }
1623
1624    /// STATEMENT: A waker panic in a mixed timer bucket leaves callback state recoverable.
1625    ///
1626    /// HOW:
1627    /// - Place a panicking waker at each position among two callback entries and catch driver unwind.
1628    /// - Check bucket and pending-ticket cleanup, then rearm a callback and verify one delivery.
1629    #[test]
1630    fn test_callback_mixed_bucket_waker_panic() {
1631        for panic_position in 0..3 {
1632            let timer = timer_state();
1633            let calls = Rc::new(Cell::new(0));
1634            let callbacks = [0, 1].map(|_| {
1635                let calls = calls.clone();
1636                CallbackTimer::new(Millis(10_000), move || calls.set(calls.get() + 1))
1637            });
1638            let ordinary = TimerHandle::new(10_000);
1639            let task = Arc::new(InPlaceWaker {
1640                panic_on_wake: true,
1641                ..InPlaceWaker::default()
1642            });
1643            let waker = Waker::from(task.clone());
1644            assert!(
1645                ordinary
1646                    .poll_elapsed(&mut Context::from_waker(&waker))
1647                    .is_pending()
1648            );
1649            timer.with_wheel(|wheel| {
1650                // Control slab drain positions, not public callback delivery order.
1651                let mut entries = [
1652                    callbacks[0].hnd.get(),
1653                    ordinary.0.get(),
1654                    callbacks[1].hnd.get(),
1655                ];
1656                entries.swap(1, panic_position);
1657                for no in entries {
1658                    wheel.unlink(no);
1659                }
1660                wheel.buckets[3] = Slab::new();
1661                for no in entries {
1662                    wheel.link(no, 3);
1663                }
1664            });
1665            timer.next_expiry.set(3);
1666            make_due(&timer);
1667            let mut driver = timer_driver(&timer);
1668            assert!(
1669                catch_unwind(AssertUnwindSafe(|| {
1670                    let _ = Pin::new(&mut driver).poll(&mut Context::from_waker(Waker::noop()));
1671                }))
1672                .is_err()
1673            );
1674            assert_eq!(task.wakes.load(Ordering::Relaxed), 1);
1675            assert_eq!(calls.get(), 0);
1676            assert!(ordinary.is_elapsed());
1677            assert!(callbacks.iter().all(CallbackTimer::is_elapsed));
1678            timer.with_wheel(|wheel| {
1679                assert!(wheel.buckets.iter().all(Slab::is_empty));
1680                assert_eq!(wheel.occupied, [0; LVL_DEPTH as usize]);
1681                assert_eq!(
1682                    wheel.callbacks.as_ref().unwrap().ready.len(),
1683                    panic_position
1684                );
1685            });
1686            drop(driver);
1687            assert!(
1688                callbacks
1689                    .iter()
1690                    .all(|callback| !callback.state.pending.get())
1691            );
1692            timer.with_wheel(|wheel| {
1693                assert!(wheel.callbacks.as_ref().unwrap().ready.is_empty());
1694            });
1695
1696            timer.insert_flags(Flags::DRIVER_STARTED);
1697            callbacks[0].reset(Millis::ZERO);
1698            make_due(&timer);
1699            let mut driver = timer_driver(&timer);
1700            for _ in 0..2 {
1701                assert!(
1702                    Pin::new(&mut driver)
1703                        .poll(&mut Context::from_waker(Waker::noop()))
1704                        .is_pending()
1705                );
1706                assert_eq!(calls.get(), 1);
1707            }
1708        }
1709    }
1710
1711    /// STATEMENT: A callback timer can be rearmed in a later system without stale delivery.
1712    ///
1713    /// HOW:
1714    /// - Carry one callback handle across two systems on a dedicated thread.
1715    /// - Verify shutdown elapses it, no callback runs before rearming, and each reset delivers once.
1716    #[test]
1717    fn test_callback_reuse_in_next_system() {
1718        std::thread::spawn(|| {
1719            let calls = Rc::new(Cell::new(0));
1720            let mut callback = None::<CallbackTimer>;
1721            for expected in 1..=2 {
1722                let calls = calls.clone();
1723                callback = Some(
1724                    ntex::rt::System::build()
1725                        .build(ntex::rt::DefaultRuntime)
1726                        .block_on(async move {
1727                            let callback = if let Some(callback) = callback {
1728                                assert!(callback.is_elapsed());
1729                                sleep(Millis(50)).await;
1730                                assert_eq!(calls.get(), expected - 1);
1731                                callback.reset(Millis::ZERO);
1732                                callback
1733                            } else {
1734                                let calls = calls.clone();
1735                                CallbackTimer::new(Millis::ZERO, move || {
1736                                    calls.set(calls.get() + 1);
1737                                })
1738                            };
1739                            sleep(Millis(50)).await;
1740                            assert_eq!(calls.get(), expected);
1741                            callback.reset(Millis(10_000));
1742                            callback
1743                        }),
1744                );
1745                assert!(callback.as_ref().unwrap().is_elapsed());
1746            }
1747        })
1748        .join()
1749        .unwrap();
1750    }
1751
1752    /// STATEMENT: Wheel access is restored after an operation panics.
1753    ///
1754    /// HOW:
1755    /// - Insert an entry inside `with_wheel`, panic, and catch the unwind.
1756    /// - Access the wheel again and verify the inserted entry remains.
1757    #[test]
1758    fn test_wheel_access_restored_on_unwind() {
1759        let timer = Timer::new();
1760        assert!(
1761            catch_unwind(AssertUnwindSafe(|| {
1762                timer.with_wheel(|wheel| {
1763                    wheel.timers.insert(entry());
1764                    panic!("wheel operation");
1765                });
1766            }))
1767            .is_err()
1768        );
1769        timer.with_wheel(|wheel| assert_eq!(wheel.timers.len(), 2));
1770    }
1771
1772    /// STATEMENT: Manual and driven expiry wake registered tasks while wheel access is held.
1773    ///
1774    /// HOW:
1775    /// - Register a waker that asserts wheel ownership and expire handles by reset, elapse, and dispatch.
1776    /// - Check all four wakes, elapsed handles, and released waker references.
1777    #[test]
1778    fn test_wake_in_place() {
1779        let timer = timer_state();
1780        let task = Arc::new(InPlaceWaker::default());
1781        let waker = Waker::from(task.clone());
1782        let mut cx = Context::from_waker(&waker);
1783        for reset in [false, true] {
1784            let handle = TimerHandle::new(10_000);
1785            assert!(handle.poll_elapsed(&mut cx).is_pending());
1786            if reset {
1787                handle.reset(0);
1788            } else {
1789                handle.elapse();
1790            }
1791            assert!(handle.is_elapsed());
1792        }
1793        let handles = [TimerHandle::new(1), TimerHandle::new(1)];
1794        for handle in &handles {
1795            assert!(handle.poll_elapsed(&mut cx).is_pending());
1796        }
1797        timer.elapsed_time.set(Some(
1798            Instant::now().checked_sub(Duration::from_secs(20)).unwrap(),
1799        ));
1800        let mut driver = timer_driver(&timer);
1801        assert!(
1802            Pin::new(&mut driver)
1803                .poll(&mut Context::from_waker(Waker::noop()))
1804                .is_pending()
1805        );
1806        assert!(handles.iter().all(TimerHandle::is_elapsed));
1807        assert_eq!(task.wakes.load(Ordering::Relaxed), 4);
1808        assert_eq!(Arc::strong_count(&task), 2);
1809    }
1810
1811    /// STATEMENT: Waker dispatch uses one time sample even if a wake callback blocks.
1812    ///
1813    /// HOW:
1814    /// - Schedule two deadlines and block inside the first wake past the second deadline.
1815    /// - Verify the first driver poll expires only one timer and the next poll expires the other.
1816    #[test]
1817    fn test_dispatch_uses_one_time_sample_for_wakers() {
1818        let timer = timer_state();
1819        let handles = [TimerHandle::new(1), TimerHandle::new(32)];
1820        timer.with_wheel(|wheel| wheel.relink(handles[1].0.get(), 3));
1821        let task = Arc::new(InPlaceWaker {
1822            delay: Duration::from_millis(64),
1823            ..InPlaceWaker::default()
1824        });
1825        let waker = Waker::from(task.clone());
1826        for handle in &handles {
1827            assert!(
1828                handle
1829                    .poll_elapsed(&mut Context::from_waker(&waker))
1830                    .is_pending()
1831            );
1832        }
1833        timer.elapsed_time.set(Some(
1834            Instant::now()
1835                .checked_sub(Duration::from_millis(32))
1836                .unwrap(),
1837        ));
1838        let mut driver = timer_driver(&timer);
1839        assert!(
1840            Pin::new(&mut driver)
1841                .poll(&mut Context::from_waker(Waker::noop()))
1842                .is_pending()
1843        );
1844        assert!(handles[0].is_elapsed());
1845        assert!(!handles[1].is_elapsed());
1846        assert_eq!(task.wakes.load(Ordering::Relaxed), 1);
1847        assert!(
1848            Pin::new(&mut driver)
1849                .poll(&mut Context::from_waker(Waker::noop()))
1850                .is_pending()
1851        );
1852        assert!(handles[1].is_elapsed());
1853        assert_eq!(task.wakes.load(Ordering::Relaxed), 2);
1854    }
1855
1856    /// STATEMENT: A wake panic clears its expired bucket and leaves driver cleanup safe.
1857    ///
1858    /// HOW:
1859    /// - Expire two same-bucket timers with a panicking waker and inspect bucket occupancy.
1860    /// - Rearm both handles, drop the driver, and verify both elapse and the driver flag clears.
1861    #[test]
1862    fn test_dispatch_panic_cleans_expired_bucket() {
1863        let timer = timer_state();
1864        let handles = [TimerHandle::new(1), TimerHandle::new(1)];
1865        timer.with_wheel(|wheel| {
1866            let bucket = wheel.timers[handles[0].0.get()].bucket.unwrap();
1867            wheel.relink(handles[1].0.get(), usize::from(bucket));
1868        });
1869        let task = Arc::new(InPlaceWaker {
1870            panic_on_wake: true,
1871            ..InPlaceWaker::default()
1872        });
1873        let waker = Waker::from(task.clone());
1874        for handle in &handles {
1875            assert!(
1876                handle
1877                    .poll_elapsed(&mut Context::from_waker(&waker))
1878                    .is_pending()
1879            );
1880        }
1881        timer.elapsed_time.set(Some(
1882            Instant::now()
1883                .checked_sub(Duration::from_millis(64))
1884                .unwrap(),
1885        ));
1886        let mut driver = timer_driver(&timer);
1887        assert!(
1888            catch_unwind(AssertUnwindSafe(|| {
1889                let _ = Pin::new(&mut driver).poll(&mut Context::from_waker(Waker::noop()));
1890            }))
1891            .is_err()
1892        );
1893        assert_eq!(task.wakes.load(Ordering::Relaxed), 1);
1894        assert!(handles.iter().all(TimerHandle::is_elapsed));
1895        timer.with_wheel(|wheel| {
1896            assert!(wheel.buckets.iter().all(Slab::is_empty));
1897            assert_eq!(wheel.occupied, [0; LVL_DEPTH as usize]);
1898        });
1899        for handle in &handles {
1900            handle.reset(10_000);
1901            assert!(!handle.is_elapsed());
1902        }
1903        drop(driver);
1904        assert!(handles.iter().all(TimerHandle::is_elapsed));
1905        assert!(!timer.flags.get().contains(Flags::DRIVER_STARTED));
1906    }
1907
1908    /// STATEMENT: A timer handle can be dropped after its thread-local wheel is destroyed.
1909    ///
1910    /// HOW:
1911    /// - Register handle storage before timer storage so its destructor runs last.
1912    /// - Leave a registered handle in that storage and verify the thread exits without panic.
1913    #[test]
1914    fn test_drop_handle_after_wheel_destroyed() {
1915        use std::cell::RefCell;
1916
1917        thread_local! {
1918            static HOLDER: RefCell<Option<TimerHandle>> = const { RefCell::new(None) };
1919        }
1920
1921        let res = std::thread::spawn(|| {
1922            // register the holder destructor first, the wheel is destroyed
1923            // before it, destructors run in reverse registration order
1924            HOLDER.with(|h| h.borrow_mut().take());
1925            ntex::rt::System::build()
1926                .build(ntex::rt::DefaultRuntime)
1927                .block_on(async {
1928                    let hnd = TimerHandle::new(1000);
1929                    std::future::poll_fn(|cx| {
1930                        assert!(hnd.poll_elapsed(cx).is_pending());
1931                        Poll::Ready(())
1932                    })
1933                    .await;
1934                    HOLDER.with(|h| *h.borrow_mut() = Some(hnd));
1935                });
1936        })
1937        .join();
1938        assert!(res.is_ok());
1939    }
1940
1941    /// STATEMENT: Callback captures are released once even when their timer outlives wheel storage.
1942    ///
1943    /// HOW:
1944    /// - Store a callback in earlier-registered thread-local storage with a counted drop capture.
1945    /// - Exit the thread and verify capture destruction occurred after wheel destruction exactly once.
1946    #[test]
1947    fn test_drop_callback_after_wheel_destroyed() {
1948        thread_local! {
1949            static HOLDER: RefCell<Option<CallbackTimer>> = const { RefCell::new(None) };
1950        }
1951
1952        let drops = Arc::new(AtomicUsize::new(0));
1953        let destroyed = Arc::new(AtomicBool::new(false));
1954        let count = drops.clone();
1955        let was_destroyed = destroyed.clone();
1956        std::thread::spawn(move || {
1957            // Register this destructor before the timer's thread-local storage.
1958            HOLDER.with(|holder| holder.borrow_mut().take());
1959            ntex::rt::System::build()
1960                .build(ntex::rt::DefaultRuntime)
1961                .block_on(async move {
1962                    let ordinary = TimerHandle::new(10_000);
1963                    let capture = OnDropFn::new(move || {
1964                        was_destroyed.store(TIMER.try_with(|_| ()).is_err(), Ordering::Relaxed);
1965                        drop(ordinary);
1966                        count.fetch_add(1, Ordering::Relaxed);
1967                    });
1968                    let callback = CallbackTimer::new(Millis(10_000), move || {
1969                        let _capture = &capture;
1970                        panic!("callback after shutdown");
1971                    });
1972                    HOLDER.with(|holder| *holder.borrow_mut() = Some(callback));
1973                });
1974        })
1975        .join()
1976        .unwrap();
1977        assert!(destroyed.load(Ordering::Relaxed));
1978        assert_eq!(drops.load(Ordering::Relaxed), 1);
1979    }
1980
1981    /// STATEMENT: Successive systems on one thread start working timers despite prior pending tasks.
1982    ///
1983    /// HOW:
1984    /// - Start three systems, complete a short sleep, and leave long sleeps pending in each.
1985    /// - Check per-system elapsed bounds and thread completion within an overall deadline.
1986    #[test]
1987    fn test_timer_in_next_system() {
1988        let res = std::thread::spawn(|| {
1989            for _ in 0..3 {
1990                let start = Instant::now();
1991                ntex::rt::System::build()
1992                    .build(ntex::rt::DefaultRuntime)
1993                    .block_on(async {
1994                        sleep(Millis(10)).await;
1995                        let _ = now();
1996                        // pending timer of the stopped system
1997                        let _hnd = sleep(Millis(10_000));
1998                        crate::spawn(async { sleep(Millis(10_000)).await });
1999                    });
2000                assert!(start.elapsed() < Duration::from_secs(5));
2001            }
2002        });
2003        let (tx, rx) = std::sync::mpsc::channel();
2004        std::thread::spawn(move || tx.send(res.join().is_ok()));
2005        assert_eq!(rx.recv_timeout(Duration::from_secs(10)), Ok(true));
2006    }
2007
2008    /// STATEMENT: Dropping stale drivers does not clear a newer generation's running flags.
2009    ///
2010    /// HOW:
2011    /// - Reset timer state, mark the new generation running, and drop both old driver types.
2012    /// - Verify all new-generation driver flags remain set.
2013    #[test]
2014    fn test_stale_driver_drop() {
2015        let timer = Rc::new(Timer::new());
2016        timer.insert_flags(Flags::RUNNING | Flags::DRIVER_STARTED | Flags::LOWRES_DRIVER);
2017        timer.reset();
2018        assert_eq!(timer.flags.get(), Flags::empty());
2019
2020        timer.insert_flags(Flags::RUNNING | Flags::DRIVER_STARTED | Flags::LOWRES_DRIVER);
2021        drop(TimerDriver {
2022            timer: timer.clone(),
2023            generation: 0,
2024            sleep: Delay::new(Duration::ZERO),
2025            armed: None,
2026        });
2027        drop(LowresTimerDriver {
2028            timer: timer.clone(),
2029            generation: 0,
2030            sleep: Delay::new(Duration::ZERO),
2031        });
2032        assert_eq!(
2033            timer.flags.get(),
2034            Flags::RUNNING | Flags::DRIVER_STARTED | Flags::LOWRES_DRIVER
2035        );
2036    }
2037
2038    fn entry() -> TimerEntry {
2039        TimerEntry {
2040            bucket: None,
2041            bucket_entry: 0,
2042            callback: false,
2043            task: None,
2044        }
2045    }
2046
2047    /// STATEMENT: Bucket placement never expires early and stays within its level's granularity.
2048    ///
2049    /// HOW:
2050    /// - Calculate buckets for varied elapsed times and exponentially increasing delays.
2051    /// - Check expiry bounds, alignment, bucket indices, and clamping beyond wheel capacity.
2052    #[test]
2053    fn test_bucket_expiry_bounds() {
2054        let timer = Timer::new();
2055        for elapsed in [0, 1, 7, 8, 63, 64, 511, 12_345, 1 << 30] {
2056            timer.elapsed.set(elapsed);
2057            let mut delta = 0;
2058            while delta < WHEEL_TIMEOUT_CUTOFF {
2059                let (idx, expiry) = timer.calc_wheel_index(elapsed + delta, delta);
2060                let lvl = (idx / LVL_SIZE as usize) as u64;
2061                assert!(expiry > elapsed + delta, "{elapsed} {delta} {expiry}");
2062                assert!(
2063                    expiry <= elapsed + delta + lvl_gran(lvl),
2064                    "{elapsed} {delta} {expiry}"
2065                );
2066                assert_eq!(expiry % lvl_gran(lvl), 0);
2067                assert_eq!(
2068                    ((expiry >> lvl_shift(lvl)) & LVL_MASK) as usize,
2069                    idx % LVL_SIZE as usize
2070                );
2071                delta = delta * 2 + 1;
2072            }
2073
2074            // clamped to the capacity of the wheel
2075            let (_, expiry) =
2076                timer.calc_wheel_index(elapsed + u64::from(u32::MAX), u64::from(u32::MAX));
2077            assert!(expiry <= elapsed + WHEEL_TIMEOUT_CUTOFF);
2078        }
2079    }
2080
2081    /// STATEMENT: Next-bucket selection visits occupied buckets in increasing expiry order.
2082    ///
2083    /// HOW:
2084    /// - Insert timers with unsorted delays and compute their expected sorted expiries.
2085    /// - Expire each selected bucket and verify its entry unlinks and the final occupancy is empty.
2086    #[test]
2087    fn test_next_pending_bucket() {
2088        let timer = Timer::new();
2089        timer.elapsed.set(1000);
2090
2091        timer.with_wheel(|w| {
2092            assert_eq!(timer.next_pending_bucket(w), None);
2093
2094            let mut expected = Vec::new();
2095            for delta in [5000, 30, 700, 100_000] {
2096                let (idx, expiry) = timer.calc_wheel_index(1000 + delta, delta);
2097                let no = w.timers.insert(entry());
2098                w.link(no, idx);
2099                expected.push((expiry, no));
2100            }
2101            expected.sort_unstable();
2102
2103            for (expiry, no) in expected {
2104                assert_eq!(timer.next_pending_bucket(w), Some(expiry));
2105                timer.elapsed.set(expiry);
2106                w.execute_expired_timers(expiry);
2107                assert!(w.timers[no].bucket.is_none());
2108            }
2109            assert_eq!(timer.next_pending_bucket(w), None);
2110            assert_eq!(w.occupied, [0; LVL_DEPTH as usize]);
2111        });
2112    }
2113
2114    /// STATEMENT: Linking, unlinking, and relinking keep bucket membership and occupancy consistent.
2115    ///
2116    /// HOW:
2117    /// - Link two entries to one bucket, remove one twice, and move the other to another level.
2118    /// - Check occupancy bits and membership after each step and after the final removal.
2119    #[test]
2120    fn test_unlink_relink() {
2121        let timer = Timer::new();
2122        timer.with_wheel(|w| {
2123            let a = w.timers.insert(entry());
2124            let b = w.timers.insert(entry());
2125            w.link(a, 3);
2126            w.link(b, 3);
2127            assert_eq!(w.occupied[0], 1 << 3);
2128
2129            assert!(w.unlink(a));
2130            assert!(!w.unlink(a));
2131            assert_eq!(w.occupied[0], 1 << 3);
2132
2133            w.relink(b, LVL_SIZE as usize + 5);
2134            assert_eq!(w.occupied[0], 0);
2135            assert_eq!(w.occupied[1], 1 << 5);
2136            assert_eq!(w.timers[b].bucket, Some(LVL_SIZE as u16 + 5));
2137
2138            assert!(w.unlink(b));
2139            assert_eq!(w.occupied, [0; LVL_DEPTH as usize]);
2140        });
2141    }
2142
2143    /// STATEMENT: Resetting a timer to zero promptly wakes its waiting task.
2144    ///
2145    /// HOW:
2146    /// - Reset a shared long timer from another task while selecting against a fallback sleep.
2147    /// - Require completion well before the fallback deadline.
2148    #[ntex::test]
2149    async fn test_reset_zero_wakes_task() {
2150        let hnd = Rc::new(TimerHandle::new(10_000));
2151        let hnd2 = hnd.clone();
2152        crate::spawn(async move { hnd2.reset(0) });
2153
2154        // the sleep wakes the task if `reset(0)` does not
2155        let start = Instant::now();
2156        crate::future::select(
2157            std::future::poll_fn(|cx| hnd.poll_elapsed(cx)),
2158            sleep(Millis(500)),
2159        )
2160        .await;
2161        let elapsed = start.elapsed();
2162        assert!(elapsed < Duration::from_millis(100), "elapsed: {elapsed:?}");
2163    }
2164
2165    /// STATEMENT: Dropping a timer driver preserves the other driver's state.
2166    ///
2167    /// HOW:
2168    /// - Drop the low-resolution driver and verify regular timer occupancy and flags remain.
2169    /// - Drop the regular driver and verify its timers clear while the low-resolution flag remains.
2170    #[test]
2171    fn test_driver_drop_keeps_other_driver() {
2172        let timer = Rc::new(Timer::new());
2173        let no = timer.with_wheel(|w| {
2174            let no = w.timers.insert(entry());
2175            w.link(no, 3);
2176            no
2177        });
2178        timer.next_expiry.set(3);
2179        timer.lowres_stime.set(Some(SystemTime::now()));
2180        timer.insert_flags(
2181            Flags::RUNNING | Flags::DRIVER_STARTED | Flags::LOWRES_DRIVER | Flags::LOWRES_TIMER,
2182        );
2183
2184        drop(LowresTimerDriver {
2185            timer: timer.clone(),
2186            generation: 0,
2187            sleep: Delay::new(Duration::ZERO),
2188        });
2189        assert_eq!(timer.flags.get(), Flags::DRIVER_STARTED);
2190        assert!(timer.lowres_stime.get().is_none());
2191        // timers are still pending
2192        assert_eq!(timer.next_expiry.get(), 3);
2193        timer.with_wheel(|w| assert_eq!(w.timers[no].bucket, Some(3)));
2194
2195        timer.insert_flags(Flags::RUNNING | Flags::LOWRES_DRIVER);
2196        drop(TimerDriver {
2197            timer: timer.clone(),
2198            generation: 0,
2199            sleep: Delay::new(Duration::ZERO),
2200            armed: None,
2201        });
2202        assert_eq!(timer.flags.get(), Flags::LOWRES_DRIVER);
2203        assert_eq!(timer.next_expiry.get(), u64::MAX);
2204        timer.with_wheel(|w| {
2205            assert!(w.timers[no].bucket.is_none());
2206            assert_eq!(w.occupied, [0; LVL_DEPTH as usize]);
2207        });
2208    }
2209
2210    /// STATEMENT: A new short timer uses current time after the runtime thread was blocked.
2211    ///
2212    /// HOW:
2213    /// - Populate the time cache, block the thread, then start a short sleep.
2214    /// - Measure with `Instant` and verify the sleep lasts at least its requested duration.
2215    #[ntex::test]
2216    async fn test_short_timer_after_blocking() {
2217        let _hnd = sleep(Millis(10_000));
2218        let _ = now();
2219
2220        // the lowres driver cannot run, the cached time goes stale
2221        std::thread::sleep(Duration::from_millis(100));
2222
2223        let start = Instant::now();
2224        sleep(Millis(50)).await;
2225        let elapsed = start.elapsed();
2226        assert!(elapsed >= Duration::from_millis(50), "elapsed: {elapsed:?}");
2227    }
2228
2229    /// STATEMENT: Cached `now()` expires even when the timer wheel becomes idle.
2230    ///
2231    /// HOW:
2232    /// - Let existing timers finish, then wait for a channel message from a sleeping thread.
2233    /// - Compare `now()` with the real clock and require less than 300 milliseconds of staleness.
2234    #[ntex::test]
2235    async fn test_driver_cached_time_expires() {
2236        // the lowres timer of the cache populated at start fires first
2237        sleep(Millis(400)).await;
2238
2239        // the wheel is idle, the thread waits for a non-timer event
2240        let (tx, rx) = async_channel::bounded(1);
2241        std::thread::spawn(move || {
2242            std::thread::sleep(Duration::from_millis(600));
2243            let _ = tx.send_blocking(());
2244        });
2245        rx.recv().await.unwrap();
2246
2247        let stale = Instant::now().saturating_duration_since(now());
2248        assert!(stale < Duration::from_millis(300), "stale: {stale:?}");
2249    }
2250
2251    /// STATEMENT: A delayed driver wake does not shift later timers by the same delay.
2252    ///
2253    /// HOW:
2254    /// - Start short and long sleeps, then block the thread past the short deadline.
2255    /// - Await both and check total elapsed time stays near the long timer's original deadline.
2256    #[ntex::test]
2257    async fn test_late_wakeup_does_not_drift() {
2258        let start = Instant::now();
2259        let fut1 = sleep(Millis(50));
2260        let fut2 = sleep(Millis(600));
2261
2262        // block the thread, the driver wakes up ~450ms late for `fut1`.
2263        // `fut2` expires at ~620ms, with drift it would expire ~550ms after
2264        // the late wakeup, i.e. after ~1050ms. The bound leaves room for
2265        // scheduling latency on loaded machines.
2266        std::thread::sleep(Duration::from_millis(500));
2267        fut1.await;
2268        fut2.await;
2269
2270        let elapsed = start.elapsed();
2271        assert!(
2272            elapsed >= Duration::from_millis(600) && elapsed < Duration::from_millis(900),
2273            "elapsed: {elapsed:?}"
2274        );
2275    }
2276
2277    /// STATEMENT: Timers with different deadlines progress alongside a recurring interval.
2278    ///
2279    /// HOW:
2280    /// - Spawn an interval task and await shorter and longer sleeps followed by another short sleep.
2281    /// - On non-macOS targets, verify each elapsed duration stays within its configured bounds.
2282    #[ntex::test]
2283    #[allow(unused_variables, clippy::used_underscore_binding)]
2284    async fn test_timer() {
2285        crate::spawn(async {
2286            let s = interval(Millis(25));
2287            loop {
2288                s.tick().await;
2289            }
2290        });
2291        let time = Instant::now();
2292        let fut1 = sleep(Millis(1000));
2293        let fut2 = sleep(Millis(200));
2294
2295        fut2.await;
2296        #[cfg(not(target_os = "macos"))]
2297        {
2298            let _elapsed = time.elapsed();
2299            assert!(
2300                _elapsed > Duration::from_millis(200) && _elapsed < Duration::from_millis(300),
2301                "elapsed: {_elapsed:?}"
2302            );
2303        }
2304
2305        fut1.await;
2306
2307        #[cfg(not(target_os = "macos"))]
2308        {
2309            let _elapsed = time.elapsed();
2310            assert!(
2311                _elapsed > Duration::from_secs(1) && _elapsed < Duration::from_millis(1200), // osx
2312                "elapsed: {_elapsed:?}",
2313            );
2314        }
2315
2316        let time = Instant::now();
2317        sleep(Millis(25)).await;
2318        #[cfg(not(target_os = "macos"))]
2319        {
2320            let _elapsed = time.elapsed();
2321            assert!(
2322                _elapsed > Duration::from_millis(20) && _elapsed < Duration::from_millis(50),
2323                "elapsed: {_elapsed:?}",
2324            );
2325        }
2326    }
2327}