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
//! 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); //! //! timer.expire(Duration::from_millis(1000)); //! assert_eq!(event.load(Ordering::SeqCst), true); //! ``` //! //! # 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] #![feature(map_first_last)] #![deny(missing_docs)] #![deny(warnings)] use alloc::boxed::Box; use alloc::collections::BTreeMap; use core::time::Duration; extern crate alloc; /// A naive timer. #[derive(Default)] pub struct Timer { events: BTreeMap<Duration, Callback>, } /// 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, mut deadline: Duration, callback: impl FnOnce(Duration) + Send + Sync + 'static, ) { while self.events.contains_key(&deadline) { deadline += Duration::from_nanos(1); } self.events.insert(deadline, Box::new(callback)); } /// Expire timers. /// /// Given the current time `now`, trigger and remove all expired timers. pub fn expire(&mut self, now: Duration) { while let Some(entry) = self.events.first_entry() { if *entry.key() > now { return; } let (_, callback) = entry.remove_entry(); callback(now); } } }