1 #![cfg_attr(not(feature = "rt"), allow(dead_code))]
2 
3 //! Source of time abstraction.
4 //!
5 //! By default, `std::time::Instant::now()` is used. However, when the
6 //! `test-util` feature flag is enabled, the values returned for `now()` are
7 //! configurable.
8 
9 cfg_not_test_util! {
10     use crate::time::{Duration, Instant};
11 
12     #[derive(Debug, Clone)]
13     pub(crate) struct Clock {}
14 
15     pub(crate) fn now() -> Instant {
16         Instant::from_std(std::time::Instant::now())
17     }
18 
19     impl Clock {
20         pub(crate) fn new(_enable_pausing: bool, _start_paused: bool) -> Clock {
21             Clock {}
22         }
23 
24         pub(crate) fn now(&self) -> Instant {
25             now()
26         }
27 
28         pub(crate) fn is_paused(&self) -> bool {
29             false
30         }
31 
32         pub(crate) fn advance(&self, _dur: Duration) {
33             unreachable!();
34         }
35     }
36 }
37 
38 cfg_test_util! {
39     use crate::time::{Duration, Instant};
40     use std::sync::{Arc, Mutex};
41 
42     cfg_rt! {
43         fn clock() -> Option<Clock> {
44             crate::runtime::context::clock()
45         }
46     }
47 
48     cfg_not_rt! {
49         fn clock() -> Option<Clock> {
50             None
51         }
52     }
53 
54     /// A handle to a source of time.
55     #[derive(Debug, Clone)]
56     pub(crate) struct Clock {
57         inner: Arc<Mutex<Inner>>,
58     }
59 
60     #[derive(Debug)]
61     struct Inner {
62         /// True if the ability to pause time is enabled.
63         enable_pausing: bool,
64 
65         /// Instant to use as the clock's base instant.
66         base: std::time::Instant,
67 
68         /// Instant at which the clock was last unfrozen
69         unfrozen: Option<std::time::Instant>,
70     }
71 
72     /// Pause time
73     ///
74     /// The current value of `Instant::now()` is saved and all subsequent calls
75     /// to `Instant::now()` until the timer wheel is checked again will return
76     /// the saved value. Once the timer wheel is checked, time will immediately
77     /// advance to the next registered `Sleep`. This is useful for running tests
78     /// that depend on time.
79     ///
80     /// Pausing time requires the `current_thread` Tokio runtime. This is the
81     /// default runtime used by `#[tokio::test]`. The runtime can be initialized
82     /// with time in a paused state using the `Builder::start_paused` method.
83     ///
84     /// # Panics
85     ///
86     /// Panics if time is already frozen or if called from outside of a
87     /// `current_thread` Tokio runtime.
88     pub fn pause() {
89         let clock = clock().expect("time cannot be frozen from outside the Tokio runtime");
90         clock.pause();
91     }
92 
93     /// Resume time
94     ///
95     /// Clears the saved `Instant::now()` value. Subsequent calls to
96     /// `Instant::now()` will return the value returned by the system call.
97     ///
98     /// # Panics
99     ///
100     /// Panics if time is not frozen or if called from outside of the Tokio
101     /// runtime.
102     pub fn resume() {
103         let clock = clock().expect("time cannot be frozen from outside the Tokio runtime");
104         let mut inner = clock.inner.lock().unwrap();
105 
106         if inner.unfrozen.is_some() {
107             panic!("time is not frozen");
108         }
109 
110         inner.unfrozen = Some(std::time::Instant::now());
111     }
112 
113     /// Advance time
114     ///
115     /// Increments the saved `Instant::now()` value by `duration`. Subsequent
116     /// calls to `Instant::now()` will return the result of the increment.
117     ///
118     /// # Panics
119     ///
120     /// Panics if time is not frozen or if called from outside of the Tokio
121     /// runtime.
122     pub async fn advance(duration: Duration) {
123         use crate::future::poll_fn;
124         use std::task::Poll;
125 
126         let clock = clock().expect("time cannot be frozen from outside the Tokio runtime");
127         clock.advance(duration);
128 
129         let mut yielded = false;
130         poll_fn(|cx| {
131             if yielded {
132                 Poll::Ready(())
133             } else {
134                 yielded = true;
135                 cx.waker().wake_by_ref();
136                 Poll::Pending
137             }
138         }).await;
139     }
140 
141     /// Return the current instant, factoring in frozen time.
142     pub(crate) fn now() -> Instant {
143         if let Some(clock) = clock() {
144             clock.now()
145         } else {
146             Instant::from_std(std::time::Instant::now())
147         }
148     }
149 
150     impl Clock {
151         /// Return a new `Clock` instance that uses the current execution context's
152         /// source of time.
153         pub(crate) fn new(enable_pausing: bool, start_paused: bool) -> Clock {
154             let now = std::time::Instant::now();
155 
156             let clock = Clock {
157                 inner: Arc::new(Mutex::new(Inner {
158                     enable_pausing,
159                     base: now,
160                     unfrozen: Some(now),
161                 })),
162             };
163 
164             if start_paused {
165                 clock.pause();
166             }
167 
168             clock
169         }
170 
171         pub(crate) fn pause(&self) {
172             let mut inner = self.inner.lock().unwrap();
173 
174             if !inner.enable_pausing {
175                 drop(inner); // avoid poisoning the lock
176                 panic!("`time::pause()` requires the `current_thread` Tokio runtime. \
177                         This is the default Runtime used by `#[tokio::test].");
178             }
179 
180             let elapsed = inner.unfrozen.as_ref().expect("time is already frozen").elapsed();
181             inner.base += elapsed;
182             inner.unfrozen = None;
183         }
184 
185         pub(crate) fn is_paused(&self) -> bool {
186             let inner = self.inner.lock().unwrap();
187             inner.unfrozen.is_none()
188         }
189 
190         pub(crate) fn advance(&self, duration: Duration) {
191             let mut inner = self.inner.lock().unwrap();
192 
193             if inner.unfrozen.is_some() {
194                 panic!("time is not frozen");
195             }
196 
197             inner.base += duration;
198         }
199 
200         pub(crate) fn now(&self) -> Instant {
201             let inner = self.inner.lock().unwrap();
202 
203             let mut ret = inner.base;
204 
205             if let Some(unfrozen) = inner.unfrozen {
206                 ret += unfrozen.elapsed();
207             }
208 
209             Instant::from_std(ret)
210         }
211     }
212 }
213