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