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
30/// The raw monotonic clock in microseconds: `CLOCK_MONOTONIC` on Linux and
31/// macOS, the performance counter on Windows. This is the value systemd's
32/// `MONOTONIC_USEC` expects (`sd_notify` reload and watchdog messages); it is
33/// not related to [`Instant`] values or to [`Clock`].
34///
35/// ```
36/// let a = net_io::monotonic_micros();
37/// let b = net_io::monotonic_micros();
38/// assert!(b >= a);
39/// ```
40#[must_use]
41pub fn monotonic_micros() -> u64 {
42    crate::sys::monotonic_micros()
43}
44
45impl Clock for MonotonicClock {
46    #[inline]
47    fn now(&self) -> Instant {
48        Instant::now()
49    }
50}
51
52/// A clock that moves only when told to, for deterministic timeout tests.
53///
54/// ```
55/// use net_io::{Clock, ManualClock};
56/// use std::time::Duration;
57/// let clock = ManualClock::new();
58/// let start = clock.now();
59/// clock.advance(Duration::from_secs(61));
60/// assert_eq!(clock.now() - start, Duration::from_secs(61));
61/// ```
62#[derive(Debug)]
63pub struct ManualClock {
64    now: Mutex<Instant>,
65}
66
67impl ManualClock {
68    /// A clock frozen at the current instant.
69    #[must_use]
70    pub fn new() -> ManualClock {
71        ManualClock {
72            now: Mutex::new(Instant::now()),
73        }
74    }
75
76    /// Moves the clock forward.
77    pub fn advance(&self, by: Duration) {
78        let mut now = self.now.lock().unwrap_or_else(PoisonError::into_inner);
79        *now = now.checked_add(by).unwrap_or(*now);
80    }
81
82    /// Moves the clock to `at`; never backwards.
83    ///
84    /// ```
85    /// use net_io::{Clock, ManualClock};
86    /// let clock = ManualClock::new();
87    /// let t = clock.now();
88    /// clock.set(t - std::time::Duration::from_secs(1));
89    /// assert_eq!(clock.now(), t);
90    /// ```
91    pub fn set(&self, at: Instant) {
92        let mut now = self.now.lock().unwrap_or_else(PoisonError::into_inner);
93        if at > *now {
94            *now = at;
95        }
96    }
97}
98
99impl Default for ManualClock {
100    fn default() -> ManualClock {
101        ManualClock::new()
102    }
103}
104
105impl Clock for ManualClock {
106    fn now(&self) -> Instant {
107        *self.now.lock().unwrap_or_else(PoisonError::into_inner)
108    }
109}
110
111#[cfg(test)]
112mod tests {
113    use super::*;
114
115    #[test]
116    fn test_manual_clock_frozen_until_advanced() {
117        let c = ManualClock::new();
118        let a = c.now();
119        std::thread::sleep(Duration::from_millis(2));
120        assert_eq!(c.now(), a);
121        c.advance(Duration::from_millis(5));
122        assert_eq!(c.now(), a + Duration::from_millis(5));
123    }
124
125    #[test]
126    fn test_manual_clock_set_forward_only() {
127        let c = ManualClock::new();
128        let a = c.now();
129        c.set(a + Duration::from_secs(3));
130        c.set(a);
131        assert_eq!(c.now(), a + Duration::from_secs(3));
132    }
133}