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}