Skip to main content

ntex_util/time/
wheel.rs

1//! Time wheel based timer service.
2//!
3//! Inspired by linux kernel timers system
4#![allow(arithmetic_overflow)]
5use std::num::NonZeroUsize;
6use std::time::{Duration, Instant, SystemTime};
7use std::{cell::Cell, cmp::max, future::Future, mem, pin::Pin, rc::Rc, task, task::Poll};
8
9use futures_timer::Delay;
10use slab::Slab;
11
12use crate::task::LocalWaker;
13
14// Clock divisor for the next level
15const LVL_CLK_SHIFT: u64 = 3;
16const LVL_CLK_DIV: u64 = 1 << LVL_CLK_SHIFT;
17const LVL_CLK_MASK: u64 = LVL_CLK_DIV - 1;
18
19const fn lvl_shift(n: u64) -> u64 {
20    n * LVL_CLK_SHIFT
21}
22
23const fn lvl_gran(n: u64) -> u64 {
24    1 << lvl_shift(n)
25}
26
27// Resolution:
28// 0: 1 millis
29// 4: ~17 millis
30const UNITS: u64 = 4;
31// const UNITS: u64 = 0;
32
33const fn to_units(n: u64) -> u64 {
34    n >> UNITS
35}
36
37const fn to_millis(n: u64) -> u64 {
38    n << UNITS
39}
40
41// The time start value for each level to select the bucket at enqueue time
42const fn lvl_start(lvl: u64) -> u64 {
43    (LVL_SIZE - 1) << ((lvl - 1) * LVL_CLK_SHIFT)
44}
45
46// Size of each clock level
47const LVL_BITS: u64 = 6;
48const LVL_SIZE: u64 = 1 << LVL_BITS;
49const LVL_MASK: u64 = LVL_SIZE - 1;
50
51// Level depth
52const LVL_DEPTH: u64 = 8;
53
54const fn lvl_offs(n: u64) -> u64 {
55    n * LVL_SIZE
56}
57
58// The cutoff (max. capacity of the wheel)
59const WHEEL_TIMEOUT_CUTOFF: u64 = lvl_start(LVL_DEPTH);
60const WHEEL_TIMEOUT_MAX: u64 = WHEEL_TIMEOUT_CUTOFF - (lvl_gran(LVL_DEPTH - 1));
61const WHEEL_SIZE: usize = (LVL_SIZE as usize) * (LVL_DEPTH as usize);
62
63// Low res time resolution
64const LOWRES_RESOLUTION: Duration = Duration::from_millis(5);
65
66const fn as_millis(dur: Duration) -> u64 {
67    dur.as_secs() * 1_000 + (dur.subsec_millis() as u64)
68}
69
70/// Returns a cached approximation of the current instant.
71///
72/// The cached value is refreshed at roughly 5 millisecond intervals.
73#[inline]
74pub fn now() -> Instant {
75    TIMER.with(|t| t.with_mod(|inner| t.now(inner)))
76}
77
78/// Returns a cached approximation of the current system time.
79///
80/// The cached value is refreshed at roughly 5 millisecond intervals.
81#[inline]
82pub fn system_time() -> SystemTime {
83    TIMER.with(|t| t.with_mod(|inner| t.system_time(inner)))
84}
85
86/// Returns the cached system time without starting the timer driver.
87///
88/// Before the cache has been initialized, this falls back to
89/// [`SystemTime::now`].
90#[inline]
91pub fn query_system_time() -> SystemTime {
92    TIMER.with(|t| t.with_mod(|inner| t.system_time(inner)))
93}
94
95#[derive(Debug)]
96/// Handle to a timer registered with ntex's local timer wheel.
97///
98/// Dropping the handle cancels the timer. A handle may be reset and reused
99/// after it has elapsed.
100pub struct TimerHandle(NonZeroUsize);
101
102impl TimerHandle {
103    /// Registers a timer that elapses after `millis`.
104    pub fn new(millis: u64) -> Self {
105        TIMER.with(|t| t.add_timer(millis))
106    }
107
108    /// Restarts the timer with a new delay in milliseconds.
109    pub fn reset(&self, millis: u64) {
110        TIMER.with(|t| t.update_timer(self.0.get(), millis));
111    }
112
113    /// Completes the timer immediately and wakes its registered task.
114    pub fn elapse(&self) {
115        TIMER.with(|t| t.remove_timer(self.0.get()));
116    }
117
118    /// Returns `true` if this timer has elapsed.
119    pub fn is_elapsed(&self) -> bool {
120        TIMER.with(|t| t.with_mod(|m| m.timers[self.0.get()].bucket.is_none()))
121    }
122
123    /// Polls until this timer has elapsed.
124    pub fn poll_elapsed(&self, cx: &mut task::Context<'_>) -> Poll<()> {
125        TIMER.with(|t| {
126            t.with_mod(|inner| {
127                let entry = &inner.timers[self.0.get()];
128                if entry.bucket.is_none() {
129                    Poll::Ready(())
130                } else {
131                    entry.task.register(cx.waker());
132                    Poll::Pending
133                }
134            })
135        })
136    }
137}
138
139impl Drop for TimerHandle {
140    fn drop(&mut self) {
141        TIMER.with(|t| t.with_mod(|inner| inner.remove_timer_bucket(self.0.get(), true)));
142    }
143}
144
145bitflags::bitflags! {
146    #[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)]
147    pub struct Flags: u8 {
148        const DRIVER_STARTED = 0b0000_0001;
149        const DRIVER_RECALC  = 0b0000_0010;
150        const LOWRES_TIMER   = 0b0000_1000;
151        const LOWRES_DRIVER  = 0b0001_0000;
152        const RUNNING        = 0b0010_0000;
153    }
154}
155
156thread_local! {
157    static TIMER: Timer = Timer::new();
158}
159
160struct Timer(Rc<TimerInner>);
161
162struct TimerInner {
163    elapsed: Cell<u64>,
164    elapsed_time: Cell<Option<Instant>>,
165    next_expiry: Cell<u64>,
166    flags: Cell<Flags>,
167    driver: LocalWaker,
168    lowres_time: Cell<Option<Instant>>,
169    lowres_stime: Cell<Option<SystemTime>>,
170    lowres_driver: LocalWaker,
171    inner: Cell<Option<Box<TimerMod>>>,
172}
173
174struct TimerMod {
175    timers: Slab<TimerEntry>,
176    driver_sleep: Delay,
177    buckets: Vec<Bucket>,
178    /// Bit field tracking which bucket currently contain entries.
179    occupied: [u64; WHEEL_SIZE],
180    lowres_driver_sleep: Delay,
181}
182
183impl Timer {
184    fn new() -> Self {
185        let mut timers = Slab::default();
186        // insert one entry, so 0 key is preoccupied
187        timers.insert(TimerEntry {
188            bucket: None,
189            bucket_entry: 0,
190            task: LocalWaker::new(),
191        });
192
193        Timer(Rc::new(TimerInner {
194            elapsed: Cell::new(0),
195            elapsed_time: Cell::new(None),
196            next_expiry: Cell::new(u64::MAX),
197            flags: Cell::new(Flags::empty()),
198            driver: LocalWaker::new(),
199            lowres_time: Cell::new(None),
200            lowres_stime: Cell::new(None),
201            lowres_driver: LocalWaker::new(),
202            inner: Cell::new(Some(Box::new(TimerMod {
203                timers,
204                buckets: Self::create_buckets(),
205                driver_sleep: Delay::new(Duration::ZERO),
206                occupied: [0; WHEEL_SIZE],
207                lowres_driver_sleep: Delay::new(Duration::ZERO),
208            }))),
209        }))
210    }
211
212    fn with_mod<F, R>(&self, f: F) -> R
213    where
214        F: FnOnce(&mut TimerMod) -> R,
215    {
216        let mut m = self.0.inner.take().unwrap();
217        let result = f(&mut m);
218        self.0.inner.set(Some(m));
219        result
220    }
221
222    fn create_buckets() -> Vec<Bucket> {
223        let mut buckets = Vec::with_capacity(WHEEL_SIZE);
224        for idx in 0..WHEEL_SIZE {
225            let lvl = idx / (LVL_SIZE as usize);
226            let offs = idx % (LVL_SIZE as usize);
227            buckets.push(Bucket::new(lvl, offs));
228        }
229        buckets
230    }
231
232    fn now(&self, inner: &mut TimerMod) -> Instant {
233        if let Some(cur) = self.0.lowres_time.get() {
234            cur
235        } else {
236            let now = Instant::now();
237
238            let flags = self.0.flags.get();
239            if flags.contains(Flags::RUNNING) {
240                self.0.lowres_time.set(Some(now));
241
242                if flags.contains(Flags::LOWRES_DRIVER) {
243                    self.0.lowres_driver.wake();
244                } else {
245                    LowresTimerDriver::start(self.0.clone(), inner);
246                }
247            }
248            now
249        }
250    }
251
252    fn system_time(&self, inner: &mut TimerMod) -> SystemTime {
253        if let Some(cur) = self.0.lowres_stime.get() {
254            cur
255        } else {
256            let now = SystemTime::now();
257            let flags = self.0.flags.get();
258
259            if flags.contains(Flags::RUNNING) {
260                self.0.lowres_stime.set(Some(now));
261
262                if flags.contains(Flags::LOWRES_DRIVER) {
263                    self.0.lowres_driver.wake();
264                } else {
265                    LowresTimerDriver::start(self.0.clone(), inner);
266                }
267            }
268            now
269        }
270    }
271
272    /// Add the timer into the hash bucket
273    fn add_timer(&self, millis: u64) -> TimerHandle {
274        self.with_mod(|inner| {
275            if millis == 0 {
276                let entry = inner.timers.vacant_entry();
277                let no = entry.key();
278
279                entry.insert(TimerEntry {
280                    bucket_entry: 0,
281                    bucket: None,
282                    task: LocalWaker::new(),
283                });
284                // SAFETY: We add TimerEntry for 0 position in constructor
285                return TimerHandle(unsafe { NonZeroUsize::new_unchecked(no) });
286            }
287
288            let mut flags = self.0.flags.get();
289            flags.insert(Flags::RUNNING);
290            self.0.flags.set(flags);
291
292            let now = self.now(inner);
293            let elapsed_time = self.0.elapsed_time();
294            let delta = if now >= elapsed_time {
295                to_units(as_millis(now - elapsed_time) + millis)
296            } else {
297                to_units(millis)
298            };
299
300            let (no, bucket_expiry) = {
301                // crate timer entry
302                let (idx, bucket_expiry) = self
303                    .0
304                    .calc_wheel_index(self.0.elapsed.get().wrapping_add(delta), delta);
305
306                let no = inner.add_entry(idx);
307                (no, bucket_expiry)
308            };
309
310            // Check whether new bucket expire earlier
311            if bucket_expiry < self.0.next_expiry.get() {
312                self.0.next_expiry.set(bucket_expiry);
313                if flags.contains(Flags::DRIVER_STARTED) {
314                    flags.insert(Flags::DRIVER_RECALC);
315                    self.0.flags.set(flags);
316                    self.0.driver.wake();
317                } else {
318                    TimerDriver::start(self.0.clone(), inner);
319                }
320            }
321
322            // SAFETY: We add TimerEntry for 0 position in constructor
323            TimerHandle(unsafe { NonZeroUsize::new_unchecked(no) })
324        })
325    }
326
327    /// Remove timer and wake task
328    fn remove_timer(&self, hnd: usize) {
329        self.with_mod(|inner| {
330            inner.remove_timer_bucket(hnd, false);
331            inner.timers[hnd].complete();
332        });
333    }
334
335    /// Update existing timer
336    fn update_timer(&self, hnd: usize, millis: u64) {
337        self.with_mod(|inner| {
338            if millis == 0 {
339                inner.remove_timer_bucket(hnd, false);
340                inner.timers[hnd].bucket = None;
341                return;
342            }
343
344            let now = self.now(inner);
345            let elapsed_time = self.0.elapsed_time();
346            let delta = if now >= elapsed_time {
347                max(to_units(as_millis(now - elapsed_time) + millis), 1)
348            } else {
349                max(to_units(millis), 1)
350            };
351
352            let bucket_expiry = {
353                // calc bucket
354                let (idx, bucket_expiry) = self
355                    .0
356                    .calc_wheel_index(self.0.elapsed.get().wrapping_add(delta), delta);
357
358                inner.update_entry(hnd, idx);
359
360                bucket_expiry
361            };
362
363            // Check whether new bucket expire earlier
364            if bucket_expiry < self.0.next_expiry.get() {
365                self.0.next_expiry.set(bucket_expiry);
366                let mut flags = self.0.flags.get();
367                if flags.contains(Flags::DRIVER_STARTED) {
368                    flags.insert(Flags::DRIVER_RECALC);
369                    self.0.flags.set(flags);
370                    self.0.driver.wake();
371                } else {
372                    TimerDriver::start(self.0.clone(), inner);
373                }
374            }
375        });
376    }
377}
378
379impl TimerMod {
380    fn execute_expired_timers(&mut self, mut clk: u64) {
381        for lvl in 0..LVL_DEPTH {
382            let idx = (clk & LVL_MASK) + lvl * LVL_SIZE;
383            let b = &mut self.buckets[idx as usize];
384            if !b.entries.is_empty() {
385                self.occupied[b.lvl as usize] &= b.bit_n;
386                for no in b.entries.drain() {
387                    if let Some(timer) = self.timers.get_mut(no) {
388                        timer.complete();
389                    }
390                }
391            }
392
393            // Is it time to look at the next level?
394            if (clk & LVL_CLK_MASK) != 0 {
395                break;
396            }
397            // Shift clock for the next level granularity
398            clk >>= LVL_CLK_SHIFT;
399        }
400    }
401
402    fn remove_timer_bucket(&mut self, handle: usize, remove_handle: bool) {
403        let entry = &mut self.timers[handle];
404        if let Some(bucket) = entry.bucket {
405            let b = &mut self.buckets[bucket as usize];
406            b.entries.remove(entry.bucket_entry);
407            if b.entries.is_empty() {
408                self.occupied[b.lvl as usize] &= b.bit_n;
409            }
410        }
411
412        if remove_handle {
413            self.timers.remove(handle);
414        }
415    }
416
417    fn add_entry(&mut self, idx: usize) -> usize {
418        let entry = self.timers.vacant_entry();
419        let no = entry.key();
420        let bucket = &mut self.buckets[idx];
421        let bucket_entry = bucket.add_entry(no);
422
423        entry.insert(TimerEntry {
424            bucket_entry,
425            bucket: Some(idx as u16),
426            task: LocalWaker::new(),
427        });
428        self.occupied[bucket.lvl as usize] |= bucket.bit;
429
430        no
431    }
432
433    fn update_entry(&mut self, hnd: usize, idx: usize) {
434        let entry = &mut self.timers[hnd];
435
436        // cleanup active timer
437        if let Some(bucket) = entry.bucket {
438            // do not do anything if wheel bucket is the same
439            if idx == bucket as usize {
440                return;
441            }
442
443            // remove timer entry from current bucket
444            let b = &mut self.buckets[bucket as usize];
445            b.entries.remove(entry.bucket_entry);
446            if b.entries.is_empty() {
447                self.occupied[b.lvl as usize] &= b.bit_n;
448            }
449        }
450
451        // put timer to new bucket
452        let bucket = &mut self.buckets[idx];
453        entry.bucket = Some(idx as u16);
454        entry.bucket_entry = bucket.add_entry(hnd);
455
456        self.occupied[bucket.lvl as usize] |= bucket.bit;
457    }
458}
459
460impl TimerInner {
461    fn with_mod<F, R>(&self, f: F) -> R
462    where
463        F: FnOnce(&mut TimerMod) -> R,
464    {
465        let mut m = self.inner.take().unwrap();
466        let result = f(&mut m);
467        self.inner.set(Some(m));
468        result
469    }
470
471    fn calc_wheel_index(&self, expires: u64, delta: u64) -> (usize, u64) {
472        if delta < lvl_start(1) {
473            Self::calc_index(expires, 0)
474        } else if delta < lvl_start(2) {
475            Self::calc_index(expires, 1)
476        } else if delta < lvl_start(3) {
477            Self::calc_index(expires, 2)
478        } else if delta < lvl_start(4) {
479            Self::calc_index(expires, 3)
480        } else if delta < lvl_start(5) {
481            Self::calc_index(expires, 4)
482        } else if delta < lvl_start(6) {
483            Self::calc_index(expires, 5)
484        } else if delta < lvl_start(7) {
485            Self::calc_index(expires, 6)
486        } else if delta < lvl_start(8) {
487            Self::calc_index(expires, 7)
488        } else {
489            // Force expire obscene large timeouts to expire at the
490            // capacity limit of the wheel.
491            if delta >= WHEEL_TIMEOUT_CUTOFF {
492                Self::calc_index(
493                    self.elapsed.get().wrapping_add(WHEEL_TIMEOUT_MAX),
494                    LVL_DEPTH - 1,
495                )
496            } else {
497                Self::calc_index(expires, LVL_DEPTH - 1)
498            }
499        }
500    }
501
502    /// Helper function to calculate the bucket index and bucket expiration
503    fn calc_index(expires: u64, lvl: u64) -> (usize, u64) {
504        // The timer wheel has to guarantee that a timer does not fire
505        // early. Early expiry can happen due to:
506        // - Timer is armed at the edge of a tick
507        // - Truncation of the expiry time in the outer wheel levels
508        //
509        // Round up with level granularity to prevent this.
510
511        let expires = (expires + lvl_gran(lvl)) >> lvl_shift(lvl);
512        (
513            (lvl_offs(lvl) + (expires & LVL_MASK)) as usize,
514            expires << lvl_shift(lvl),
515        )
516    }
517
518    fn elapsed_time(&self) -> Instant {
519        if let Some(elapsed_time) = self.elapsed_time.get() {
520            elapsed_time
521        } else {
522            let elapsed_time = Instant::now();
523            self.elapsed_time.set(Some(elapsed_time));
524            elapsed_time
525        }
526    }
527
528    fn execute_expired_timers(&self, inner: &mut TimerMod) {
529        inner.execute_expired_timers(self.next_expiry.get());
530    }
531
532    /// Find next expiration bucket
533    fn next_pending_bucket(&self, inner: &mut TimerMod) -> Option<u64> {
534        let mut clk = self.elapsed.get();
535        let mut next = u64::MAX;
536
537        for lvl in 0..LVL_DEPTH {
538            let lvl_clk = clk & LVL_CLK_MASK;
539            let occupied = inner.occupied[lvl as usize];
540            let pos = if occupied == 0 {
541                -1
542            } else {
543                let zeros = occupied
544                    .rotate_right((clk & LVL_MASK) as u32)
545                    .trailing_zeros() as usize;
546                zeros as isize
547            };
548
549            if pos >= 0 {
550                let tmp = (clk + pos as u64) << lvl_shift(lvl);
551                if tmp < next {
552                    next = tmp;
553                }
554
555                // If the next expiration happens before we reach
556                // the next level, no need to check further.
557                if (pos as u64) <= ((LVL_CLK_DIV - lvl_clk) & LVL_CLK_MASK) {
558                    break;
559                }
560            }
561
562            clk >>= LVL_CLK_SHIFT;
563            clk += u64::from(lvl_clk != 0);
564        }
565
566        if next < u64::MAX { Some(next) } else { None }
567    }
568
569    /// Get next expiry time in millis
570    fn next_expiry_ms(&self) -> u64 {
571        to_millis(self.next_expiry.get().saturating_sub(self.elapsed.get()))
572    }
573
574    fn stop_wheel(&self) {
575        // mark all timers as elapsed
576        if let Some(mut inner) = self.inner.take() {
577            let mut buckets = mem::take(&mut inner.buckets);
578            for b in &mut buckets {
579                for no in b.entries.drain() {
580                    inner.timers[no].bucket = None;
581                }
582            }
583
584            // cleanup info
585            self.flags.set(Flags::empty());
586            self.next_expiry.set(u64::MAX);
587            self.elapsed.set(0);
588            self.elapsed_time.set(None);
589            self.lowres_time.set(None);
590            self.lowres_stime.set(None);
591
592            inner.buckets = buckets;
593            inner.occupied = [0; WHEEL_SIZE];
594            self.inner.set(Some(inner));
595        }
596    }
597}
598
599#[derive(Debug)]
600struct Bucket {
601    lvl: u32,
602    bit: u64,
603    bit_n: u64,
604    entries: Slab<usize>,
605}
606
607impl Bucket {
608    fn add_entry(&mut self, no: usize) -> usize {
609        self.entries.insert(no)
610    }
611}
612
613impl Bucket {
614    fn new(lvl: usize, offs: usize) -> Self {
615        let bit = 1 << (offs as u64);
616        Bucket {
617            bit,
618            lvl: lvl as u32,
619            bit_n: !bit,
620            entries: Slab::default(),
621        }
622    }
623}
624
625#[derive(Debug)]
626struct TimerEntry {
627    bucket: Option<u16>,
628    bucket_entry: usize,
629    task: LocalWaker,
630}
631
632impl TimerEntry {
633    fn complete(&mut self) {
634        if self.bucket.is_some() {
635            self.bucket.take();
636            self.task.wake();
637        }
638    }
639}
640
641struct TimerDriver(Rc<TimerInner>);
642
643impl TimerDriver {
644    fn start(timer: Rc<TimerInner>, inner: &mut TimerMod) {
645        let mut flags = timer.flags.get();
646        flags.insert(Flags::DRIVER_STARTED);
647        timer.flags.set(flags);
648        inner.driver_sleep = Delay::new(Duration::from_millis(timer.next_expiry_ms()));
649
650        crate::spawn(TimerDriver(timer));
651    }
652}
653
654impl Drop for TimerDriver {
655    fn drop(&mut self) {
656        self.0.stop_wheel();
657    }
658}
659
660impl Future for TimerDriver {
661    type Output = ();
662
663    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
664        self.0.driver.register(cx.waker());
665
666        self.0.with_mod(|inner| {
667            let mut flags = self.0.flags.get();
668            if flags.contains(Flags::DRIVER_RECALC) {
669                flags.remove(Flags::DRIVER_RECALC);
670                self.0.flags.set(flags);
671
672                let now = Instant::now();
673                let deadline = if let Some(diff) = now.checked_duration_since(self.0.elapsed_time())
674                {
675                    Duration::from_millis(self.0.next_expiry_ms()).saturating_sub(diff)
676                } else {
677                    Duration::from_millis(self.0.next_expiry_ms())
678                };
679                inner.driver_sleep.reset(deadline);
680            }
681
682            loop {
683                if Pin::new(&mut inner.driver_sleep).poll(cx).is_ready() {
684                    let now = Instant::now();
685                    self.0.elapsed.set(self.0.next_expiry.get());
686                    self.0.elapsed_time.set(Some(now));
687                    self.0.execute_expired_timers(inner);
688
689                    if let Some(next_expiry) = self.0.next_pending_bucket(inner) {
690                        self.0.next_expiry.set(next_expiry);
691                        let dur = Duration::from_millis(self.0.next_expiry_ms());
692                        inner.driver_sleep.reset(dur);
693                        continue;
694                    }
695                    self.0.next_expiry.set(u64::MAX);
696                    self.0.elapsed_time.set(None);
697                }
698                return Poll::Pending;
699            }
700        })
701    }
702}
703
704struct LowresTimerDriver(Rc<TimerInner>);
705
706impl LowresTimerDriver {
707    fn start(timer: Rc<TimerInner>, inner: &mut TimerMod) {
708        let mut flags = timer.flags.get();
709        flags.insert(Flags::LOWRES_DRIVER);
710        timer.flags.set(flags);
711        inner.lowres_driver_sleep = Delay::new(LOWRES_RESOLUTION);
712
713        crate::spawn(LowresTimerDriver(timer));
714    }
715}
716
717impl Drop for LowresTimerDriver {
718    fn drop(&mut self) {
719        self.0.stop_wheel();
720    }
721}
722
723impl Future for LowresTimerDriver {
724    type Output = ();
725
726    fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
727        self.0.lowres_driver.register(cx.waker());
728
729        self.0.with_mod(|inner| {
730            let mut flags = self.0.flags.get();
731            if !flags.contains(Flags::LOWRES_TIMER) {
732                flags.insert(Flags::LOWRES_TIMER);
733                self.0.flags.set(flags);
734                inner.lowres_driver_sleep.reset(LOWRES_RESOLUTION);
735            }
736
737            if Pin::new(&mut inner.lowres_driver_sleep).poll(cx).is_ready() {
738                self.0.lowres_time.set(None);
739                self.0.lowres_stime.set(None);
740                flags.remove(Flags::LOWRES_TIMER);
741                self.0.flags.set(flags);
742            }
743            Poll::Pending
744        })
745    }
746}
747
748#[cfg(test)]
749mod tests {
750    use super::*;
751    use crate::time::{Millis, interval, sleep};
752
753    #[ntex::test]
754    #[allow(unused_variables, clippy::used_underscore_binding)]
755    async fn test_timer() {
756        crate::spawn(async {
757            let s = interval(Millis(25));
758            loop {
759                s.tick().await;
760            }
761        });
762        let time = Instant::now();
763        let fut1 = sleep(Millis(1000));
764        let fut2 = sleep(Millis(200));
765
766        fut2.await;
767        #[cfg(not(target_os = "macos"))]
768        {
769            let _elapsed = time.elapsed();
770            assert!(
771                _elapsed > Duration::from_millis(200) && _elapsed < Duration::from_millis(300),
772                "elapsed: {_elapsed:?}"
773            );
774        }
775
776        fut1.await;
777
778        #[cfg(not(target_os = "macos"))]
779        {
780            let _elapsed = time.elapsed();
781            assert!(
782                _elapsed > Duration::from_secs(1) && _elapsed < Duration::from_millis(1200), // osx
783                "elapsed: {_elapsed:?}",
784            );
785        }
786
787        let time = Instant::now();
788        sleep(Millis(25)).await;
789        #[cfg(not(target_os = "macos"))]
790        {
791            let _elapsed = time.elapsed();
792            assert!(
793                _elapsed > Duration::from_millis(20) && _elapsed < Duration::from_millis(50),
794                "elapsed: {_elapsed:?}",
795            );
796        }
797    }
798}