mio_timer/
timer.rs

1//! Timer optimized for I/O related operations
2use super::convert;
3use lazycell::LazyCell;
4use log::trace;
5use mio::{Interest, Registry, Token, Waker, event::Source};
6use slab::Slab;
7use std::{
8    cmp, fmt, io, iter,
9    sync::{
10        Arc, Mutex,
11        atomic::{AtomicUsize, Ordering},
12    },
13    thread,
14    time::{Duration, Instant},
15    u64, usize,
16};
17
18type Tick = u64;
19
20const TICK_MAX: Tick = u64::MAX;
21
22// Manages communication with wakeup thread
23type WakeupState = Arc<AtomicUsize>;
24
25const TERMINATE_THREAD: usize = 0;
26const EMPTY: Token = Token(usize::MAX);
27
28/// A timer.
29///
30/// Typical usage goes like this:
31///
32/// * register the timer with a `mio::Poll`.
33/// * set a timeout, by calling `Timer::set_timeout`.  Here you provide some
34///   state to be associated with this timeout.
35/// * poll the `Poll`, to learn when a timeout has occurred.
36/// * retrieve state associated with the timeout by calling `Timer::poll`.
37///
38/// You can omit use of the `Poll` altogether, if you like, and just poll the
39/// `Timer` directly.
40pub struct Timer<T> {
41    // Size of each tick in milliseconds
42    tick_ms: u64,
43    // Slab of timeout entries
44    entries: Slab<Entry<T>>,
45    // Timeout wheel. Each tick, the timer will look at the next slot for
46    // timeouts that match the current tick.
47    wheel: Vec<WheelEntry>,
48    // Tick 0's time instant
49    start: Instant,
50    // The current tick
51    tick: Tick,
52    // The next entry to possibly timeout
53    next: Token,
54    // Masks the target tick to get the slot
55    mask: u64,
56    // Set on registration with Poll
57    inner: LazyCell<Inner>,
58}
59
60/// Used to create a `Timer`.
61pub struct Builder {
62    // Approximate duration of each tick
63    tick: Duration,
64    // Number of slots in the timer wheel
65    num_slots: usize,
66    // Max number of timeouts that can be in flight at a given time.
67    capacity: usize,
68}
69
70impl Builder {
71    /// Set the tick duration.  Default is 100ms.
72    pub fn tick_duration(mut self, duration: Duration) -> Builder {
73        self.tick = duration;
74        self
75    }
76
77    /// Set the number of slots.  Default is 256.
78    pub fn num_slots(mut self, num_slots: usize) -> Builder {
79        self.num_slots = num_slots;
80        self
81    }
82
83    /// Set the capacity.  Default is 65536.
84    pub fn capacity(mut self, capacity: usize) -> Builder {
85        self.capacity = capacity;
86        self
87    }
88
89    /// Build a `Timer` with the parameters set on this `Builder`.
90    pub fn build<T>(self) -> Timer<T> {
91        Timer::new(
92            convert::millis(self.tick),
93            self.num_slots,
94            self.capacity,
95            Instant::now(),
96        )
97    }
98}
99
100impl Default for Builder {
101    fn default() -> Builder {
102        Builder {
103            tick: Duration::from_millis(100),
104            num_slots: 1 << 8,
105            capacity: 1 << 16,
106        }
107    }
108}
109
110/// A timeout, as returned by `Timer::set_timeout`.
111///
112/// Use this as the argument to `Timer::cancel_timeout`, to cancel this timeout.
113#[derive(Clone, Debug)]
114pub struct Timeout {
115    // Reference into the timer entry slab
116    token: Token,
117    // Tick that it should match up with
118    tick: u64,
119}
120
121struct Inner {
122    waker: Arc<Mutex<Option<Waker>>>,
123    wakeup_state: WakeupState,
124    wakeup_thread: thread::JoinHandle<()>,
125}
126
127impl Drop for Inner {
128    fn drop(&mut self) {
129        // 1. Set wakeup state to TERMINATE_THREAD
130        self.wakeup_state.store(TERMINATE_THREAD, Ordering::Release);
131        // 2. Wake him up
132        self.wakeup_thread.thread().unpark();
133    }
134}
135
136impl fmt::Debug for Inner {
137    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
138        fmt.debug_struct("Inner")
139            .field("wakeup_state", &self.wakeup_state.load(Ordering::Relaxed))
140            .finish()
141    }
142}
143
144#[derive(Copy, Clone, Debug)]
145struct WheelEntry {
146    next_tick: Tick,
147    head: Token,
148}
149
150// Doubly linked list of timer entries. Allows for efficient insertion /
151// removal of timeouts.
152struct Entry<T> {
153    state: T,
154    links: EntryLinks,
155}
156
157impl<T> Entry<T> {
158    fn new(state: T, tick: u64, next: Token) -> Entry<T> {
159        Entry {
160            state,
161            links: EntryLinks {
162                tick,
163                prev: EMPTY,
164                next,
165            },
166        }
167    }
168}
169
170#[derive(Copy, Clone)]
171struct EntryLinks {
172    tick: Tick,
173    prev: Token,
174    next: Token,
175}
176
177impl<T> Timer<T> {
178    fn new(tick_ms: u64, num_slots: usize, capacity: usize, start: Instant) -> Timer<T> {
179        let num_slots = num_slots.next_power_of_two();
180        let capacity = capacity.next_power_of_two();
181        let mask = (num_slots as u64) - 1;
182        let wheel = iter::repeat(WheelEntry {
183            next_tick: TICK_MAX,
184            head: EMPTY,
185        })
186        .take(num_slots)
187        .collect();
188
189        Timer {
190            tick_ms,
191            entries: Slab::with_capacity(capacity),
192            wheel,
193            start,
194            tick: 0,
195            next: EMPTY,
196            mask,
197            inner: LazyCell::new(),
198        }
199    }
200
201    /// Set a timeout.
202    ///
203    /// When the timeout occurs, the given state becomes available via `poll`.
204    pub fn set_timeout(&mut self, delay_from_now: Duration, state: T) -> Timeout {
205        let delay_from_start = self.start.elapsed() + delay_from_now;
206        self.set_timeout_at(delay_from_start, state)
207    }
208
209    fn set_timeout_at(&mut self, delay_from_start: Duration, state: T) -> Timeout {
210        let mut tick = duration_to_tick(delay_from_start, self.tick_ms);
211        trace!(
212            "setting timeout; delay={:?}; tick={:?}; current-tick={:?}",
213            delay_from_start, tick, self.tick
214        );
215
216        // Always target at least 1 tick in the future
217        if tick <= self.tick {
218            tick = self.tick + 1;
219        }
220
221        self.insert(tick, state)
222    }
223
224    fn insert(&mut self, tick: Tick, state: T) -> Timeout {
225        // Get the slot for the requested tick
226        let slot = self.slot_for(tick);
227        let curr = self.wheel[slot];
228
229        // Insert the new entry
230        let entry = Entry::new(state, tick, curr.head);
231        let token = Token(self.entries.insert(entry));
232
233        if curr.head != EMPTY {
234            // If there was a previous entry, set its prev pointer to the new
235            // entry
236            self.entries[curr.head.into()].links.prev = token;
237        }
238
239        // Update the head slot
240        self.wheel[slot] = WheelEntry {
241            next_tick: cmp::min(tick, curr.next_tick),
242            head: token,
243        };
244
245        self.schedule_readiness(tick);
246
247        trace!("inserted timout; slot={}; token={:?}", slot, token);
248
249        // Return the new timeout
250        Timeout { token, tick }
251    }
252
253    /// Cancel a timeout.
254    ///
255    /// If the timeout has not yet occurred, the return value holds the
256    /// associated state.
257    pub fn cancel_timeout(&mut self, timeout: &Timeout) -> Option<T> {
258        let links = match self.entries.get(timeout.token.into()) {
259            Some(e) => e.links,
260            None => return None,
261        };
262
263        // Sanity check
264        if links.tick != timeout.tick {
265            return None;
266        }
267
268        self.unlink(&links, timeout.token);
269        Some(self.entries.remove(timeout.token.into()).state)
270    }
271
272    /// Poll for an expired timer.
273    ///
274    /// The return value holds the state associated with the first expired
275    /// timer, if any.
276    pub fn poll(&mut self) -> Option<T> {
277        let target_tick = current_tick(self.start, self.tick_ms);
278        self.poll_to(target_tick)
279    }
280
281    fn poll_to(&mut self, mut target_tick: Tick) -> Option<T> {
282        trace!(
283            "tick_to; target_tick={}; current_tick={}",
284            target_tick, self.tick
285        );
286
287        if target_tick < self.tick {
288            target_tick = self.tick;
289        }
290
291        while self.tick <= target_tick {
292            let curr = self.next;
293
294            trace!("ticking; curr={:?}", curr);
295
296            if curr == EMPTY {
297                self.tick += 1;
298
299                let slot = self.slot_for(self.tick);
300                self.next = self.wheel[slot].head;
301
302                // Handle the case when a slot has a single timeout which gets
303                // canceled before the timeout expires. In this case, the
304                // slot's head is EMPTY but there is a value for next_tick. Not
305                // resetting next_tick here causes the timer to get stuck in a
306                // loop.
307                if self.next == EMPTY {
308                    self.wheel[slot].next_tick = TICK_MAX;
309                }
310            } else {
311                let slot = self.slot_for(self.tick);
312
313                if curr == self.wheel[slot].head {
314                    self.wheel[slot].next_tick = TICK_MAX;
315                }
316
317                let links = self.entries[curr.into()].links;
318
319                if links.tick <= self.tick {
320                    trace!("triggering; token={:?}", curr);
321
322                    // Unlink will also advance self.next
323                    self.unlink(&links, curr);
324
325                    // Remove and return the token
326                    return Some(self.entries.remove(curr.into()).state);
327                } else {
328                    let next_tick = self.wheel[slot].next_tick;
329                    self.wheel[slot].next_tick = cmp::min(next_tick, links.tick);
330                    self.next = links.next;
331                }
332            }
333        }
334
335        // No more timeouts to poll
336        if let Some(_inner) = self.inner.borrow() {
337            trace!("unsetting readiness");
338            // let _ = inner.set_readiness.set_readiness(Ready::empty());
339
340            if let Some(tick) = self.next_tick() {
341                self.schedule_readiness(tick);
342            }
343        }
344
345        None
346    }
347
348    fn unlink(&mut self, links: &EntryLinks, token: Token) {
349        trace!(
350            "unlinking timeout; slot={}; token={:?}",
351            self.slot_for(links.tick),
352            token
353        );
354
355        if links.prev == EMPTY {
356            let slot = self.slot_for(links.tick);
357            self.wheel[slot].head = links.next;
358        } else {
359            self.entries[links.prev.into()].links.next = links.next;
360        }
361
362        if links.next != EMPTY {
363            self.entries[links.next.into()].links.prev = links.prev;
364
365            if token == self.next {
366                self.next = links.next;
367            }
368        } else if token == self.next {
369            self.next = EMPTY;
370        }
371    }
372
373    fn schedule_readiness(&self, tick: Tick) {
374        if let Some(inner) = self.inner.borrow() {
375            // Coordinate setting readiness w/ the wakeup thread
376            let mut curr = inner.wakeup_state.load(Ordering::Acquire);
377
378            loop {
379                if curr as Tick <= tick {
380                    // Nothing to do, wakeup is already scheduled
381                    return;
382                }
383
384                // Attempt to move the wakeup time forward
385                trace!("advancing the wakeup time; target={}; curr={}", tick, curr);
386                let actual = inner
387                    .wakeup_state
388                    .compare_exchange(curr, tick as usize, Ordering::Release, Ordering::Relaxed)
389                    .unwrap_or_else(|x| x);
390
391                if actual == curr {
392                    // Signal to the wakeup thread that the wakeup time has
393                    // been changed.
394                    trace!("unparking wakeup thread");
395                    inner.wakeup_thread.thread().unpark();
396                    return;
397                }
398
399                curr = actual;
400            }
401        }
402    }
403
404    // Next tick containing a timeout
405    fn next_tick(&self) -> Option<Tick> {
406        if self.next != EMPTY {
407            let slot = self.slot_for(self.entries[self.next.into()].links.tick);
408
409            if self.wheel[slot].next_tick == self.tick {
410                // There is data ready right now
411                return Some(self.tick);
412            }
413        }
414
415        self.wheel.iter().map(|e| e.next_tick).min()
416    }
417
418    fn slot_for(&self, tick: Tick) -> usize {
419        (self.mask & tick) as usize
420    }
421}
422
423impl<T> Default for Timer<T> {
424    fn default() -> Timer<T> {
425        Builder::default().build()
426    }
427}
428
429impl<T> Source for Timer<T> {
430    fn register(&mut self, registry: &Registry, token: Token, _: Interest) -> io::Result<()> {
431        if self.inner.borrow().is_some() {
432            return Err(io::Error::new(
433                io::ErrorKind::Other,
434                "timer already registered",
435            ));
436        }
437
438        let waker = Arc::new(Mutex::new(Some(Waker::new(registry, token)?)));
439        let wakeup_state = Arc::new(AtomicUsize::new(usize::MAX));
440        let thread_handle = spawn_wakeup_thread(
441            Arc::clone(&wakeup_state),
442            waker.clone(),
443            self.start,
444            self.tick_ms,
445        );
446
447        self.inner
448            .fill(Inner {
449                waker,
450                wakeup_state,
451                wakeup_thread: thread_handle,
452            })
453            .expect("timer already registered");
454
455        if let Some(next_tick) = self.next_tick() {
456            self.schedule_readiness(next_tick);
457        }
458
459        Ok(())
460    }
461
462    fn reregister(&mut self, registry: &Registry, token: Token, _: Interest) -> io::Result<()> {
463        match self.inner.borrow() {
464            Some(inner) => {
465                let mut waker = inner.waker.lock().unwrap();
466                *waker = Some(Waker::new(registry, token)?);
467
468                return Ok(());
469            }
470            None => Err(io::Error::new(
471                io::ErrorKind::Other,
472                "receiver not registered",
473            )),
474        }
475    }
476
477    fn deregister(&mut self, _: &Registry) -> io::Result<()> {
478        match self.inner.borrow() {
479            Some(inner) => {
480                let mut waker = inner.waker.lock().unwrap();
481                *waker = None;
482
483                return Ok(());
484            }
485            None => Err(io::Error::new(
486                io::ErrorKind::Other,
487                "receiver not registered",
488            )),
489        }
490    }
491}
492
493fn spawn_wakeup_thread(
494    state: WakeupState,
495    waker: Arc<Mutex<Option<Waker>>>,
496    start: Instant,
497    tick_ms: u64,
498) -> thread::JoinHandle<()> {
499    thread::spawn(move || {
500        let mut sleep_until_tick = state.load(Ordering::Acquire) as Tick;
501
502        loop {
503            if sleep_until_tick == TERMINATE_THREAD as Tick {
504                return;
505            }
506
507            let now_tick = current_tick(start, tick_ms);
508
509            trace!(
510                "wakeup thread: sleep_until_tick={:?}; now_tick={:?}",
511                sleep_until_tick, now_tick
512            );
513
514            if now_tick < sleep_until_tick {
515                // Calling park_timeout with u64::MAX leads to undefined
516                // behavior in pthread, causing the park to return immediately
517                // and causing the thread to tightly spin. Instead of u64::MAX
518                // on large values, simply use a blocking park.
519                match tick_ms.checked_mul(sleep_until_tick - now_tick) {
520                    Some(sleep_duration) => {
521                        trace!(
522                            "sleeping; tick_ms={}; now_tick={}; sleep_until_tick={}; duration={:?}",
523                            tick_ms, now_tick, sleep_until_tick, sleep_duration
524                        );
525                        thread::park_timeout(Duration::from_millis(sleep_duration));
526                    }
527                    None => {
528                        trace!(
529                            "sleeping; tick_ms={}; now_tick={}; blocking sleep",
530                            tick_ms, now_tick
531                        );
532                        thread::park();
533                    }
534                }
535                sleep_until_tick = state.load(Ordering::Acquire) as Tick;
536            } else {
537                let actual = state
538                    .compare_exchange(
539                        sleep_until_tick as usize,
540                        usize::MAX,
541                        Ordering::AcqRel,
542                        Ordering::Acquire,
543                    )
544                    .unwrap_or_else(|x| x) as Tick;
545
546                if actual == sleep_until_tick {
547                    trace!("setting readiness from wakeup thread");
548                    if let Some(waker) = &mut *waker.lock().unwrap() {
549                        let _ = waker.wake();
550                    }
551                    sleep_until_tick = usize::MAX as Tick;
552                } else {
553                    sleep_until_tick = actual as Tick;
554                }
555            }
556        }
557    })
558}
559
560fn duration_to_tick(elapsed: Duration, tick_ms: u64) -> Tick {
561    // Calculate tick rounding up to the closest one
562    let elapsed_ms = convert::millis(elapsed);
563    elapsed_ms.saturating_add(tick_ms / 2) / tick_ms
564}
565
566fn current_tick(start: Instant, tick_ms: u64) -> Tick {
567    duration_to_tick(start.elapsed(), tick_ms)
568}
569
570#[cfg(test)]
571mod test {
572    use super::*;
573    use std::time::{Duration, Instant};
574
575    #[test]
576    pub fn test_timeout_next_tick() {
577        let mut t = timer();
578        let mut tick;
579
580        t.set_timeout_at(Duration::from_millis(100), "a");
581
582        tick = ms_to_tick(&t, 50);
583        assert_eq!(None, t.poll_to(tick));
584
585        tick = ms_to_tick(&t, 100);
586        assert_eq!(Some("a"), t.poll_to(tick));
587        assert_eq!(None, t.poll_to(tick));
588
589        tick = ms_to_tick(&t, 150);
590        assert_eq!(None, t.poll_to(tick));
591
592        tick = ms_to_tick(&t, 200);
593        assert_eq!(None, t.poll_to(tick));
594
595        assert_eq!(count(&t), 0);
596    }
597
598    #[test]
599    pub fn test_clearing_timeout() {
600        let mut t = timer();
601        let mut tick;
602
603        let to = t.set_timeout_at(Duration::from_millis(100), "a");
604        assert_eq!("a", t.cancel_timeout(&to).unwrap());
605
606        tick = ms_to_tick(&t, 100);
607        assert_eq!(None, t.poll_to(tick));
608
609        tick = ms_to_tick(&t, 200);
610        assert_eq!(None, t.poll_to(tick));
611
612        assert_eq!(count(&t), 0);
613    }
614
615    #[test]
616    pub fn test_multiple_timeouts_same_tick() {
617        let mut t = timer();
618        let mut tick;
619
620        t.set_timeout_at(Duration::from_millis(100), "a");
621        t.set_timeout_at(Duration::from_millis(100), "b");
622
623        let mut rcv = vec![];
624
625        tick = ms_to_tick(&t, 100);
626        rcv.push(t.poll_to(tick).unwrap());
627        rcv.push(t.poll_to(tick).unwrap());
628
629        assert_eq!(None, t.poll_to(tick));
630
631        rcv.sort();
632        assert!(rcv == ["a", "b"], "actual={:?}", rcv);
633
634        tick = ms_to_tick(&t, 200);
635        assert_eq!(None, t.poll_to(tick));
636
637        assert_eq!(count(&t), 0);
638    }
639
640    #[test]
641    pub fn test_multiple_timeouts_diff_tick() {
642        let mut t = timer();
643        let mut tick;
644
645        t.set_timeout_at(Duration::from_millis(110), "a");
646        t.set_timeout_at(Duration::from_millis(220), "b");
647        t.set_timeout_at(Duration::from_millis(230), "c");
648        t.set_timeout_at(Duration::from_millis(440), "d");
649        t.set_timeout_at(Duration::from_millis(560), "e");
650
651        tick = ms_to_tick(&t, 100);
652        assert_eq!(Some("a"), t.poll_to(tick));
653        assert_eq!(None, t.poll_to(tick));
654
655        tick = ms_to_tick(&t, 200);
656        assert_eq!(Some("c"), t.poll_to(tick));
657        assert_eq!(Some("b"), t.poll_to(tick));
658        assert_eq!(None, t.poll_to(tick));
659
660        tick = ms_to_tick(&t, 300);
661        assert_eq!(None, t.poll_to(tick));
662
663        tick = ms_to_tick(&t, 400);
664        assert_eq!(Some("d"), t.poll_to(tick));
665        assert_eq!(None, t.poll_to(tick));
666
667        tick = ms_to_tick(&t, 500);
668        assert_eq!(None, t.poll_to(tick));
669
670        tick = ms_to_tick(&t, 600);
671        assert_eq!(Some("e"), t.poll_to(tick));
672        assert_eq!(None, t.poll_to(tick));
673    }
674
675    #[test]
676    pub fn test_catching_up() {
677        let mut t = timer();
678
679        t.set_timeout_at(Duration::from_millis(110), "a");
680        t.set_timeout_at(Duration::from_millis(220), "b");
681        t.set_timeout_at(Duration::from_millis(230), "c");
682        t.set_timeout_at(Duration::from_millis(440), "d");
683
684        let tick = ms_to_tick(&t, 600);
685        assert_eq!(Some("a"), t.poll_to(tick));
686        assert_eq!(Some("c"), t.poll_to(tick));
687        assert_eq!(Some("b"), t.poll_to(tick));
688        assert_eq!(Some("d"), t.poll_to(tick));
689        assert_eq!(None, t.poll_to(tick));
690    }
691
692    #[test]
693    pub fn test_timeout_hash_collision() {
694        let mut t = timer();
695        let mut tick;
696
697        t.set_timeout_at(Duration::from_millis(100), "a");
698        t.set_timeout_at(Duration::from_millis(100 + TICK * SLOTS as u64), "b");
699
700        tick = ms_to_tick(&t, 100);
701        assert_eq!(Some("a"), t.poll_to(tick));
702        assert_eq!(1, count(&t));
703
704        tick = ms_to_tick(&t, 200);
705        assert_eq!(None, t.poll_to(tick));
706        assert_eq!(1, count(&t));
707
708        tick = ms_to_tick(&t, 100 + TICK * SLOTS as u64);
709        assert_eq!(Some("b"), t.poll_to(tick));
710        assert_eq!(0, count(&t));
711    }
712
713    #[test]
714    pub fn test_clearing_timeout_between_triggers() {
715        let mut t = timer();
716        let mut tick;
717
718        let a = t.set_timeout_at(Duration::from_millis(100), "a");
719        let _ = t.set_timeout_at(Duration::from_millis(100), "b");
720        let _ = t.set_timeout_at(Duration::from_millis(200), "c");
721
722        tick = ms_to_tick(&t, 100);
723        assert_eq!(Some("b"), t.poll_to(tick));
724        assert_eq!(2, count(&t));
725
726        t.cancel_timeout(&a);
727        assert_eq!(1, count(&t));
728
729        assert_eq!(None, t.poll_to(tick));
730
731        tick = ms_to_tick(&t, 200);
732        assert_eq!(Some("c"), t.poll_to(tick));
733        assert_eq!(0, count(&t));
734    }
735
736    const TICK: u64 = 100;
737    const SLOTS: usize = 16;
738    const CAPACITY: usize = 32;
739
740    fn count<T>(timer: &Timer<T>) -> usize {
741        timer.entries.len()
742    }
743
744    fn timer() -> Timer<&'static str> {
745        Timer::new(TICK, SLOTS, CAPACITY, Instant::now())
746    }
747
748    fn ms_to_tick<T>(timer: &Timer<T>, ms: u64) -> u64 {
749        ms / timer.tick_ms
750    }
751}