monotonic_clock/
clock.rs

1use crate::epoch::Epoch;
2use ::std::time::Duration;
3
4/// # Monotonic Clock
5///  A monotonic clock that can be anchored to a specific [Epoch].
6/// The clock is guaranteed to be monotonic, but not necessarily
7/// continuous.
8///
9///
10/// ## Thread safety
11/// The clock is thread safe.
12///
13/// Eventually, we want to have network synchronization, but for now, we
14/// just use the system clock.
15/// TODO: Add network synchronization.
16///
17/// ## Example
18/// ```
19/// use monotonic_clock::Clock;
20/// use std::thread;
21/// use std::time::Duration;
22/// let clock = Clock::new();
23/// let start = clock.now();
24/// thread::sleep(Duration::from_millis(100));
25/// let end = clock.now();
26/// assert!(end - start >= Duration::from_millis(100));
27/// ```
28///
29
30pub trait MonotonicClock {
31    /// Return the epoch of the clock.
32    fn epoch(&self) -> Epoch;
33
34    /// Returns the current time.
35    fn now(&self) -> Duration;
36
37    /// Returns true if the clock is ticking.
38    fn is_ticking(&self) -> bool;
39
40    /// Get the now time since the epoch.
41    #[inline]
42    fn time(&self) -> Duration {
43        self.now() + *self.epoch()
44    }
45
46    /// Get the now time since the epoch as a float.
47    #[inline]
48    fn time_as_float(&self) -> f64 {
49        let time = self.time();
50        time.as_secs() as f64 + time.subsec_nanos() as f64 * 1e-9
51    }
52
53    /// Get the now time since the epoch as a float.
54    /// This is a convenience function for `clock_as_float`.
55    /// It is provided for compatibility with the `time` crate.
56    #[inline]
57    fn as_float(&self) -> f64 {
58        self.time_as_float()
59    }
60}
61
62/// A monotonic clock that can be anchored to a specific [Epoch].
63
64#[derive(Debug, Clone)]
65pub struct Clock {
66    inner: ::std::sync::Arc<::std::sync::RwLock<InnerClock>>,
67}
68
69unsafe impl Sync for Clock {}
70unsafe impl Send for Clock {}
71
72impl Clock {
73    /// Create a new clock.
74    pub fn new() -> Self {
75        Self {
76            inner: ::std::sync::Arc::new(::std::sync::RwLock::new(InnerClock::new())),
77        }
78    }
79
80    /// Get current time on the clock.
81    pub fn now(&self) -> Duration {
82        self.inner.read().unwrap().now()
83    }
84    
85    /// Start the clock.
86    pub fn start(&self) {
87        self.inner.write().unwrap().start();
88    }
89
90    /// Stop the clock.
91    pub fn stop(&self) -> Option<Duration> {
92        self.inner.write().unwrap().stop()
93    }
94
95    /// Reset the clock.
96    pub fn reset(&self) {
97        self.inner.write().unwrap().reset();
98    }
99
100    /// Resume a paused clock.
101    pub fn resume(&self) -> Option<Duration> {
102        self.inner.write().unwrap().resume()
103    }
104}
105
106impl MonotonicClock for Clock {
107    fn epoch(&self) -> Epoch {
108        self.inner.read().unwrap().epoch()
109    }
110
111    fn now(&self) -> Duration {
112        self.inner.read().unwrap().now()
113    }
114
115    fn is_ticking(&self) -> bool {
116        self.inner.read().unwrap().is_ticking()
117    }
118}
119
120impl Default for Clock {
121    #[inline]
122    fn default() -> Self {
123        Self::new()
124    }
125}
126
127#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
128struct InnerClock {
129    epoch: Epoch, // The unix_epoch time at which the clock was created.
130    start: ::std::time::Instant,
131    stop: Option<::std::time::Instant>,
132}
133
134impl InnerClock {
135    /// Create a new monotonic clock.
136    #[inline]
137    pub fn new() -> Self {
138        Self {
139            epoch: Epoch::from_unix(),
140            start: ::std::time::Instant::now(),
141            stop: None,
142        }
143    }
144
145    /// Returns the epoch of the clock.
146    #[inline]
147    pub fn epoch(&self) -> Epoch {
148        self.epoch
149    }
150
151    /// Reset the clock to zero.
152    #[inline]
153    pub fn reset(&mut self) {
154        self.epoch = Epoch::from_unix();
155        self.start = ::std::time::Instant::now();
156        self.stop = None;
157    }
158
159    /// Start the clock.
160    #[inline]
161    pub fn start(&mut self) {
162        self.start = ::std::time::Instant::now();
163        self.stop = None;
164    }
165
166    /// Resumes paused clock.
167    /// If the clock is not stopped, this does nothing.
168
169    #[inline]
170    pub fn resume(&mut self) -> Option<Duration> {
171        if let Some(stop) = self.stop {
172            self.stop = None;
173            ::std::time::Instant::now().checked_duration_since(stop)
174        } else {
175            Some(Duration::new(0, 0))
176        }
177    }
178
179    /// Stop the clock if it's running, otherwise does nothing.
180    /// Returns the duration the clock was running.
181    #[inline]
182    pub fn stop(&mut self) -> Option<Duration> {
183        if self.stop.is_none() {
184            self.stop = Some(::std::time::Instant::now());
185        }
186        self.stop.map(|stop| stop - self.start)
187    }
188
189    /// Get duration since the clock has been running time.
190    #[inline]
191    pub fn now(&self) -> Duration {
192        if let Some(stop) = self.stop {
193            stop.duration_since(self.start)
194        } else {
195            ::std::time::Instant::now().duration_since(self.start)
196        }
197    }
198
199    /// Is the clock running?
200    #[inline]
201    pub fn is_ticking(&self) -> bool {
202        self.stop.is_none()
203    }
204}
205
206impl Default for InnerClock {
207    #[inline]
208    fn default() -> Self {
209        Self::new()
210    }
211}
212
213impl MonotonicClock for InnerClock {
214    #[inline]
215    fn epoch(&self) -> Epoch {
216        self.epoch
217    }
218
219    #[inline]
220    fn now(&self) -> Duration {
221        self.now()
222    }
223
224    #[inline]
225    fn is_ticking(&self) -> bool {
226        self.is_ticking()
227    }
228}
229
230impl ::std::fmt::Display for InnerClock {
231    fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
232        write!(f, "{}", self.time_as_float())
233    }
234}
235
236impl ::std::convert::From<InnerClock> for Duration {
237    /// Get the now time since the clock's epoch.
238    fn from(mc: InnerClock) -> Self {
239        mc.time()
240    }
241}
242
243#[cfg(test)]
244mod tests {
245    use super::*;
246    use assert2::assert;
247    #[test]
248    fn test_monotonic_clock() {
249        let clock = Clock::new();
250        assert!(clock.now() < Duration::from_secs(1));
251        ::std::thread::sleep(Duration::from_secs(1));
252        assert!(clock.now() > Duration::from_secs(1));
253        ::std::thread::sleep(Duration::from_secs(2));
254
255        let stopped_at = clock.stop().unwrap();
256        assert!(clock.now() > Duration::from_secs(2));
257        assert!(clock.now() == stopped_at);
258
259        ::std::thread::sleep(Duration::from_secs(2));
260        assert!(clock.now() > Duration::from_secs(2));
261        assert!(clock.now() == stopped_at);
262
263        clock.resume();
264        ::std::thread::sleep(Duration::from_secs(1));
265        assert!(clock.now() > stopped_at);
266        clock.reset();
267        assert!(clock.now() < Duration::from_secs(1));
268    }
269
270    #[test]
271    fn test_monotonic_clock_since_unix_epoch() {
272        let clock = Clock::new();
273        eprintln!("clock.epoch = {:?}", clock.epoch());
274        eprintln!("clock.now() = {:?}", clock.time());
275    }
276}