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}