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
//! //! # simpler_timer //! //! A simple timer mechanism to track arbitrary timeouts. //! It doesn't do anything fancy, e.g. no callbacks upon expiry, just give it a Duration //! and poll if the timer is expired. Timers can be reset and reused //! for periodic contexts, such as a simple time based control loop. //! //! # Example //! //! ``` //! use std::time::Duration; //! //! // 100ms timer //! let tick = Timer::with_duration(Duration::from_millis(100)); //! // 1 sec timer //! let end = Timer::with_duration(Duration::from_secs(1)); //! //! loop { //! if tick.expired() { //! // do something interesting //! println!("tick"); //! tick.reset(); //! } //! //! if end.expired() { //! // don't reset, let's get out of here //! break; //! } //! } //! //! println!("total time: {}ms", end.elapsed().as_millis()); //! //! ``` //! //! use std::cell::Cell; use std::time::{Duration, Instant}; /// Timer provides extremely basic timing abilities #[derive(Debug, Clone)] pub struct Timer { instant: Cell<Instant>, duration: Duration, } impl Timer { /// Creates a new timer of zero `Duration`. /// /// Similar to `std::time::Instant` as this is really only useful /// for getting `elapsed` time since `reset` pub fn new() -> Timer { Timer { instant: Cell::new(Instant::now()), duration: Duration::from_secs(0), } } /// Creates a new timer with `duration` length pub fn with_duration(duration: Duration) -> Timer { let mut timer = Timer::new(); timer.duration = duration; timer } /// Resets the timer. /// /// # Note /// The decision was made intentionally to only require a `&self` for /// resetting a timer so that another object can own a `Timer` and not require /// `&mut self` of the object owning the timer. /// /// `elapsed()` will start over at 0 after a `reset()` pub fn reset(&self) { self.instant.set(Instant::now()); } /// Check if the timer is expired /// /// `expired` = `elapsed` >= `duration` pub fn expired(&self) -> bool { self.instant.get().elapsed() >= self.duration } /// Return a `Duration` of the configured time of the Timer pub fn duration(&self) -> Duration { self.duration } /// Block execution until the timer expires. /// /// - If the timer is already expired, this returns immediately pub fn wait(&self) { if let Some(duration) = self.duration.checked_sub(self.instant.get().elapsed()) { std::thread::sleep(duration); } } /// Get `Duration` of time elapsed since `Timer` `reset` /// /// # Note /// A newly constructed timer is considered to be `reset` pub fn elapsed(&self) -> Duration { self.instant.get().elapsed() } } #[cfg(test)] mod tests { use super::*; #[test] #[ignore] fn wait_test() { let timer = Timer::with_duration(Duration::from_millis(500)); timer.wait(); assert!(timer.elapsed().as_millis() >= 500); } // This test is really poor as it relies on actual time and not a mocked time, so results are unpredictable #[test] #[ignore] fn wait_should_account_for_elapsed_time() { let timer = Timer::with_duration(Duration::from_millis(50)); std::thread::sleep(Duration::from_millis(25)); let pre_wait = timer.elapsed(); timer.wait(); let diff = timer.elapsed() - pre_wait; let diff = diff.as_millis(); assert!(diff < 50); assert!(diff >= 25); } }