broker_tokio/time/
clock.rs

1//! Source of time abstraction.
2//!
3//! By default, `std::time::Instant::now()` is used. However, when the
4//! `test-util` feature flag is enabled, the values returned for `now()` are
5//! configurable.
6
7cfg_not_test_util! {
8    use crate::time::{Duration, Instant};
9
10    #[derive(Debug, Clone)]
11    pub(crate) struct Clock {}
12
13    pub(crate) fn now() -> Instant {
14        Instant::from_std(std::time::Instant::now())
15    }
16
17    impl Clock {
18        pub(crate) fn new() -> Clock {
19            Clock {}
20        }
21
22        pub(crate) fn now(&self) -> Instant {
23            now()
24        }
25
26        pub(crate) fn is_frozen(&self) -> bool {
27            false
28        }
29
30        pub(crate) fn advance(&self, _dur: Duration) {
31            unreachable!();
32        }
33    }
34}
35
36cfg_test_util! {
37    use crate::time::{Duration, Instant};
38    use std::sync::{Arc, Mutex};
39    use crate::runtime::context;
40
41    /// A handle to a source of time.
42    #[derive(Debug, Clone)]
43    pub(crate) struct Clock {
44        inner: Arc<Inner>,
45    }
46
47    #[derive(Debug)]
48    struct Inner {
49        /// Instant at which the clock was created
50        start: std::time::Instant,
51
52        /// Current, "frozen" time as an offset from `start`.
53        frozen: Mutex<Option<Duration>>,
54    }
55
56    /// Pause time
57    ///
58    /// The current value of `Instant::now()` is saved and all subsequent calls
59    /// to `Instant::now()` will return the saved value. This is useful for
60    /// running tests that are dependent on time.
61    ///
62    /// # Panics
63    ///
64    /// Panics if time is already frozen or if called from outside of the Tokio
65    /// runtime.
66    pub fn pause() {
67        let clock = context::clock().expect("time cannot be frozen from outside the Tokio runtime");
68        let mut frozen = clock.inner.frozen.lock().unwrap();
69        if frozen.is_some() {
70            panic!("time is already frozen");
71        }
72        *frozen = Some(clock.inner.start.elapsed());
73    }
74
75    /// Resume time
76    ///
77    /// Clears the saved `Instant::now()` value. Subsequent calls to
78    /// `Instant::now()` will return the value returned by the system call.
79    ///
80    /// # Panics
81    ///
82    /// Panics if time is not frozen or if called from outside of the Tokio
83    /// runtime.
84    pub fn resume() {
85        let clock = context::clock().expect("time cannot be frozen from outside the Tokio runtime");
86        let mut frozen = clock.inner.frozen.lock().unwrap();
87
88        if frozen.is_none() {
89            panic!("time is not frozen");
90        }
91
92        *frozen = None;
93    }
94
95    /// Advance time
96    ///
97    /// Increments the saved `Instant::now()` value by `duration`. Subsequent
98    /// calls to `Instant::now()` will return the result of the increment.
99    ///
100    /// # Panics
101    ///
102    /// Panics if time is not frozen or if called from outside of the Tokio
103    /// runtime.
104    pub async fn advance(duration: Duration) {
105        let clock = context::clock().expect("time cannot be frozen from outside the Tokio runtime");
106        clock.advance(duration);
107        crate::task::yield_now().await;
108    }
109
110    /// Return the current instant, factoring in frozen time.
111    pub(crate) fn now() -> Instant {
112        if let Some(clock) = context::clock() {
113            if let Some(frozen) = *clock.inner.frozen.lock().unwrap() {
114                Instant::from_std(clock.inner.start + frozen)
115            } else {
116                Instant::from_std(std::time::Instant::now())
117            }
118        } else {
119            Instant::from_std(std::time::Instant::now())
120        }
121    }
122
123    impl Clock {
124        /// Return a new `Clock` instance that uses the current execution context's
125        /// source of time.
126        pub(crate) fn new() -> Clock {
127            Clock {
128                inner: Arc::new(Inner {
129                    start: std::time::Instant::now(),
130                    frozen: Mutex::new(None),
131                }),
132            }
133        }
134
135        // TODO: delete this. Some tests rely on this
136        #[cfg(all(test, not(loom)))]
137        /// Return a new `Clock` instance that uses the current execution context's
138        /// source of time.
139        pub(crate) fn new_frozen() -> Clock {
140            Clock {
141                inner: Arc::new(Inner {
142                    start: std::time::Instant::now(),
143                    frozen: Mutex::new(Some(Duration::from_millis(0))),
144                }),
145            }
146        }
147
148        pub(crate) fn is_frozen(&self) -> bool {
149            self.inner.frozen.lock().unwrap().is_some()
150        }
151
152        pub(crate) fn advance(&self, duration: Duration) {
153            let mut frozen = self.inner.frozen.lock().unwrap();
154
155            if let Some(ref mut elapsed) = *frozen {
156                *elapsed += duration;
157            } else {
158                panic!("time is not frozen");
159            }
160        }
161
162        // TODO: delete this as well
163        #[cfg(all(test, not(loom)))]
164        pub(crate) fn advanced(&self) -> Duration {
165            self.inner.frozen.lock().unwrap().unwrap()
166        }
167
168        pub(crate) fn now(&self) -> Instant {
169            Instant::from_std(if let Some(frozen) = *self.inner.frozen.lock().unwrap() {
170                self.inner.start + frozen
171            } else {
172                std::time::Instant::now()
173            })
174        }
175    }
176}