naive_timer/
lib.rs

1//! A minimal naive timer for embedded (no_std) platforms.
2//!
3//! # Example: single thread
4//! ```
5//! use alloc::sync::Arc;
6//! use core::time::Duration;
7//! use core::sync::atomic::{AtomicBool, Ordering};
8//! use naive_timer::Timer;
9//! extern crate alloc;
10//!
11//! let mut timer = Timer::default();
12//! let event = Arc::new(AtomicBool::new(false));
13//!
14//! timer.add(Duration::from_secs(1), {
15//!     let event = event.clone();
16//!     move |now| event.store(true, Ordering::SeqCst)
17//! });
18//!
19//! timer.expire(Duration::from_millis(999));
20//! assert_eq!(event.load(Ordering::SeqCst), false);
21//! assert_eq!(timer.next(), Some(Duration::from_secs(1)));
22//!
23//! timer.expire(Duration::from_millis(1000));
24//! assert_eq!(event.load(Ordering::SeqCst), true);
25//! assert_eq!(timer.next(), None);
26//! ```
27//!
28//! # Example: ticks and wakeup
29//! ```
30//! use alloc::sync::Arc;
31//! use core::time::Duration;
32//! use core::sync::atomic::{AtomicU64, Ordering};
33//! use std::time::{SystemTime, UNIX_EPOCH};
34//! use naive_timer::Timer;
35//! extern crate alloc;
36//!
37//! /// Get current time in `Duration`.
38//! fn now() -> Duration {
39//!     SystemTime::now().duration_since(UNIX_EPOCH).unwrap()
40//! }
41//!
42//! let mut timer = Timer::default();
43//!
44//! // add timer to wake me up
45//! let thread = std::thread::current();
46//! timer.add(now() + Duration::from_millis(25), move |_| thread.unpark());
47//!
48//! // generate ticks (5 times per 10ms)
49//! // spawn a thread to emulate timer interrupt
50//! let handle = std::thread::spawn(move || {
51//!     for _ in 0..5 {
52//!         std::thread::sleep(Duration::from_millis(10));
53//!         timer.expire(now());
54//!     }
55//! });
56//!
57//! // wait for wakeup
58//! let t0 = now();
59//! std::thread::park();
60//! let sleep_time = now() - t0;
61//! assert!(sleep_time > Duration::from_millis(30));
62//! assert!(sleep_time < Duration::from_millis(40));
63//!
64//! // join thread
65//! handle.join().unwrap();
66//! ```
67//!
68//! # Limitations
69//!
70//! For simplicity, **timer cancellation** is not supported.
71//!
72//! The callback function should check the current time `now` and its own information,
73//! to decide whether it is still a valid event.
74
75#![no_std]
76#![deny(missing_docs)]
77#![deny(warnings)]
78
79use alloc::boxed::Box;
80use alloc::collections::BinaryHeap;
81use core::cmp::Ordering;
82use core::time::Duration;
83
84extern crate alloc;
85
86/// A naive timer.
87#[derive(Default)]
88pub struct Timer {
89    events: BinaryHeap<Event>,
90}
91
92/// The type of callback function.
93type Callback = Box<dyn FnOnce(Duration) + Send + Sync + 'static>;
94
95impl Timer {
96    /// Add a timer.
97    ///
98    /// The `callback` will be called on timer expired after `deadline`.
99    pub fn add(
100        &mut self,
101        deadline: Duration,
102        callback: impl FnOnce(Duration) + Send + Sync + 'static,
103    ) {
104        let event = Event {
105            deadline,
106            callback: Box::new(callback),
107        };
108        self.events.push(event);
109    }
110
111    /// Expire timers.
112    ///
113    /// Given the current time `now`, trigger and remove all expired timers.
114    pub fn expire(&mut self, now: Duration) {
115        while let Some(t) = self.events.peek() {
116            if t.deadline > now {
117                break;
118            }
119            let event = self.events.pop().unwrap();
120            (event.callback)(now);
121        }
122    }
123
124    /// Get next timer.
125    pub fn next(&self) -> Option<Duration> {
126        self.events.peek().map(|e| e.deadline)
127    }
128}
129
130struct Event {
131    deadline: Duration,
132    callback: Callback,
133}
134
135impl PartialEq for Event {
136    fn eq(&self, other: &Self) -> bool {
137        self.deadline.eq(&other.deadline)
138    }
139}
140
141impl Eq for Event {}
142
143// BinaryHeap is a max-heap. So we need to reverse the order.
144impl PartialOrd for Event {
145    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
146        other.deadline.partial_cmp(&self.deadline)
147    }
148}
149
150impl Ord for Event {
151    fn cmp(&self, other: &Event) -> Ordering {
152        other.deadline.cmp(&self.deadline)
153    }
154}