Skip to main content

net_io/
time.rs

1//! Clocks. Deadlines read time through a [`Clock`] so tests can move it by hand.
2
3use std::sync::Mutex;
4use std::sync::PoisonError;
5use std::time::{Duration, Instant};
6
7/// A source of monotonic time.
8///
9/// # Examples
10///
11/// ```
12/// use net_io::{Clock, MonotonicClock};
13/// let a = MonotonicClock.now();
14/// assert!(MonotonicClock.now() >= a);
15/// ```
16pub trait Clock: Send + Sync + 'static {
17    /// The current instant.
18    fn now(&self) -> Instant;
19}
20
21/// `Instant::now()`.
22///
23/// ```
24/// use net_io::Clock;
25/// let _ = net_io::MonotonicClock.now();
26/// ```
27#[derive(Clone, Copy, Debug, Default)]
28pub struct MonotonicClock;
29
30impl Clock for MonotonicClock {
31    #[inline]
32    fn now(&self) -> Instant {
33        Instant::now()
34    }
35}
36
37/// A clock that moves only when told to, for deterministic timeout tests.
38///
39/// ```
40/// use net_io::{Clock, ManualClock};
41/// use std::time::Duration;
42/// let clock = ManualClock::new();
43/// let start = clock.now();
44/// clock.advance(Duration::from_secs(61));
45/// assert_eq!(clock.now() - start, Duration::from_secs(61));
46/// ```
47#[derive(Debug)]
48pub struct ManualClock {
49    now: Mutex<Instant>,
50}
51
52impl ManualClock {
53    /// A clock frozen at the current instant.
54    #[must_use]
55    pub fn new() -> ManualClock {
56        ManualClock {
57            now: Mutex::new(Instant::now()),
58        }
59    }
60
61    /// Moves the clock forward.
62    pub fn advance(&self, by: Duration) {
63        let mut now = self.now.lock().unwrap_or_else(PoisonError::into_inner);
64        *now = now.checked_add(by).unwrap_or(*now);
65    }
66
67    /// Moves the clock to `at`; never backwards.
68    ///
69    /// ```
70    /// use net_io::{Clock, ManualClock};
71    /// let clock = ManualClock::new();
72    /// let t = clock.now();
73    /// clock.set(t - std::time::Duration::from_secs(1));
74    /// assert_eq!(clock.now(), t);
75    /// ```
76    pub fn set(&self, at: Instant) {
77        let mut now = self.now.lock().unwrap_or_else(PoisonError::into_inner);
78        if at > *now {
79            *now = at;
80        }
81    }
82}
83
84impl Default for ManualClock {
85    fn default() -> ManualClock {
86        ManualClock::new()
87    }
88}
89
90impl Clock for ManualClock {
91    fn now(&self) -> Instant {
92        *self.now.lock().unwrap_or_else(PoisonError::into_inner)
93    }
94}
95
96#[cfg(test)]
97mod tests {
98    use super::*;
99
100    #[test]
101    fn test_manual_clock_frozen_until_advanced() {
102        let c = ManualClock::new();
103        let a = c.now();
104        std::thread::sleep(Duration::from_millis(2));
105        assert_eq!(c.now(), a);
106        c.advance(Duration::from_millis(5));
107        assert_eq!(c.now(), a + Duration::from_millis(5));
108    }
109
110    #[test]
111    fn test_manual_clock_set_forward_only() {
112        let c = ManualClock::new();
113        let a = c.now();
114        c.set(a + Duration::from_secs(3));
115        c.set(a);
116        assert_eq!(c.now(), a + Duration::from_secs(3));
117    }
118}