• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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::{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 }
29 
30 cfg_test_util! {
31     use crate::time::{Duration, Instant};
32     use crate::loom::sync::Mutex;
33     use crate::loom::sync::atomic::Ordering;
34     use std::sync::atomic::AtomicBool as StdAtomicBool;
35 
36     cfg_rt! {
37         #[track_caller]
38         fn with_clock<R>(f: impl FnOnce(Option<&Clock>) -> Result<R, &'static str>) -> R {
39             use crate::runtime::Handle;
40 
41             let res = match Handle::try_current() {
42                 Ok(handle) => f(Some(handle.inner.driver().clock())),
43                 Err(ref e) if e.is_missing_context() => f(None),
44                 Err(_) => panic!("{}", crate::util::error::THREAD_LOCAL_DESTROYED_ERROR),
45             };
46 
47             match res {
48                 Ok(ret) => ret,
49                 Err(msg) => panic!("{}", msg),
50             }
51         }
52     }
53 
54     cfg_not_rt! {
55         #[track_caller]
56         fn with_clock<R>(f: impl FnOnce(Option<&Clock>) -> Result<R, &'static str>) -> R {
57             match f(None) {
58                 Ok(ret) => ret,
59                 Err(msg) => panic!("{}", msg),
60             }
61         }
62     }
63 
64     /// A handle to a source of time.
65     #[derive(Debug)]
66     pub(crate) struct Clock {
67         inner: Mutex<Inner>,
68     }
69 
70     // Used to track if the clock was ever paused. This is an optimization to
71     // avoid touching the mutex if `test-util` was accidentally enabled in
72     // release mode.
73     //
74     // A static is used so we can avoid accessing the thread-local as well. The
75     // `std` AtomicBool is used directly because loom does not support static
76     // atomics.
77     static DID_PAUSE_CLOCK: StdAtomicBool = StdAtomicBool::new(false);
78 
79     #[derive(Debug)]
80     struct Inner {
81         /// True if the ability to pause time is enabled.
82         enable_pausing: bool,
83 
84         /// Instant to use as the clock's base instant.
85         base: std::time::Instant,
86 
87         /// Instant at which the clock was last unfrozen.
88         unfrozen: Option<std::time::Instant>,
89 
90         /// Number of `inhibit_auto_advance` calls still in effect.
91         auto_advance_inhibit_count: usize,
92     }
93 
94     /// Pauses time.
95     ///
96     /// The current value of `Instant::now()` is saved and all subsequent calls
97     /// to `Instant::now()` will return the saved value. The saved value can be
98     /// changed by [`advance`] or by the time auto-advancing once the runtime
99     /// has no work to do. This only affects the `Instant` type in Tokio, and
100     /// the `Instant` in std continues to work as normal.
101     ///
102     /// Pausing time requires the `current_thread` Tokio runtime. This is the
103     /// default runtime used by `#[tokio::test]`. The runtime can be initialized
104     /// with time in a paused state using the `Builder::start_paused` method.
105     ///
106     /// For cases where time is immediately paused, it is better to pause
107     /// the time using the `main` or `test` macro:
108     /// ```
109     /// #[tokio::main(flavor = "current_thread", start_paused = true)]
110     /// async fn main() {
111     ///    println!("Hello world");
112     /// }
113     /// ```
114     ///
115     /// # Panics
116     ///
117     /// Panics if time is already frozen or if called from outside of a
118     /// `current_thread` Tokio runtime.
119     ///
120     /// # Auto-advance
121     ///
122     /// If time is paused and the runtime has no work to do, the clock is
123     /// auto-advanced to the next pending timer. This means that [`Sleep`] or
124     /// other timer-backed primitives can cause the runtime to advance the
125     /// current time when awaited.
126     ///
127     /// [`Sleep`]: crate::time::Sleep
128     /// [`advance`]: crate::time::advance
129     #[track_caller]
130     pub fn pause() {
131         with_clock(|maybe_clock| {
132             match maybe_clock {
133                 Some(clock) => clock.pause(),
134                 None => Err("time cannot be frozen from outside the Tokio runtime"),
135             }
136         });
137     }
138 
139     /// Resumes time.
140     ///
141     /// Clears the saved `Instant::now()` value. Subsequent calls to
142     /// `Instant::now()` will return the value returned by the system call.
143     ///
144     /// # Panics
145     ///
146     /// Panics if time is not frozen or if called from outside of the Tokio
147     /// runtime.
148     #[track_caller]
149     pub fn resume() {
150         with_clock(|maybe_clock| {
151             let clock = match maybe_clock {
152                 Some(clock) => clock,
153                 None => return Err("time cannot be frozen from outside the Tokio runtime"),
154             };
155 
156             let mut inner = clock.inner.lock();
157 
158             if inner.unfrozen.is_some() {
159                 return Err("time is not frozen");
160             }
161 
162             inner.unfrozen = Some(std::time::Instant::now());
163             Ok(())
164         });
165     }
166 
167     /// Advances time.
168     ///
169     /// Increments the saved `Instant::now()` value by `duration`. Subsequent
170     /// calls to `Instant::now()` will return the result of the increment.
171     ///
172     /// This function will make the current time jump forward by the given
173     /// duration in one jump. This means that all `sleep` calls with a deadline
174     /// before the new time will immediately complete "at the same time", and
175     /// the runtime is free to poll them in any order.  Additionally, this
176     /// method will not wait for the `sleep` calls it advanced past to complete.
177     /// If you want to do that, you should instead call [`sleep`] and rely on
178     /// the runtime's auto-advance feature.
179     ///
180     /// Note that calls to `sleep` are not guaranteed to complete the first time
181     /// they are polled after a call to `advance`. For example, this can happen
182     /// if the runtime has not yet touched the timer driver after the call to
183     /// `advance`. However if they don't, the runtime will poll the task again
184     /// shortly.
185     ///
186     /// # Panics
187     ///
188     /// Panics if time is not frozen or if called from outside of the Tokio
189     /// runtime.
190     ///
191     /// # Auto-advance
192     ///
193     /// If the time is paused and there is no work to do, the runtime advances
194     /// time to the next timer. See [`pause`](pause#auto-advance) for more
195     /// details.
196     ///
197     /// [`sleep`]: fn@crate::time::sleep
198     pub async fn advance(duration: Duration) {
199         with_clock(|maybe_clock| {
200             let clock = match maybe_clock {
201                 Some(clock) => clock,
202                 None => return Err("time cannot be frozen from outside the Tokio runtime"),
203             };
204 
205             clock.advance(duration)
206         });
207 
208         crate::task::yield_now().await;
209     }
210 
211     /// Returns the current instant, factoring in frozen time.
212     pub(crate) fn now() -> Instant {
213         if !DID_PAUSE_CLOCK.load(Ordering::Acquire) {
214             return Instant::from_std(std::time::Instant::now());
215         }
216 
217         with_clock(|maybe_clock| {
218             Ok(if let Some(clock) = maybe_clock {
219                 clock.now()
220             } else {
221                 Instant::from_std(std::time::Instant::now())
222             })
223         })
224     }
225 
226     impl Clock {
227         /// Returns a new `Clock` instance that uses the current execution context's
228         /// source of time.
229         pub(crate) fn new(enable_pausing: bool, start_paused: bool) -> Clock {
230             let now = std::time::Instant::now();
231 
232             let clock = Clock {
233                 inner: Mutex::new(Inner {
234                     enable_pausing,
235                     base: now,
236                     unfrozen: Some(now),
237                     auto_advance_inhibit_count: 0,
238                 }),
239             };
240 
241             if start_paused {
242                 if let Err(msg) = clock.pause() {
243                     panic!("{}", msg);
244                 }
245             }
246 
247             clock
248         }
249 
250         pub(crate) fn pause(&self) -> Result<(), &'static str> {
251             let mut inner = self.inner.lock();
252 
253             if !inner.enable_pausing {
254                 drop(inner); // avoid poisoning the lock
255                 return Err("`time::pause()` requires the `current_thread` Tokio runtime. \
256                         This is the default Runtime used by `#[tokio::test].");
257             }
258 
259             // Track that we paused the clock
260             DID_PAUSE_CLOCK.store(true, Ordering::Release);
261 
262             let elapsed = match inner.unfrozen.as_ref() {
263                 Some(v) => v.elapsed(),
264                 None => return Err("time is already frozen")
265             };
266             inner.base += elapsed;
267             inner.unfrozen = None;
268 
269             Ok(())
270         }
271 
272         /// Temporarily stop auto-advancing the clock (see `tokio::time::pause`).
273         pub(crate) fn inhibit_auto_advance(&self) {
274             let mut inner = self.inner.lock();
275             inner.auto_advance_inhibit_count += 1;
276         }
277 
278         pub(crate) fn allow_auto_advance(&self) {
279             let mut inner = self.inner.lock();
280             inner.auto_advance_inhibit_count -= 1;
281         }
282 
283         pub(crate) fn can_auto_advance(&self) -> bool {
284             let inner = self.inner.lock();
285             inner.unfrozen.is_none() && inner.auto_advance_inhibit_count == 0
286         }
287 
288         pub(crate) fn advance(&self, duration: Duration) -> Result<(), &'static str> {
289             let mut inner = self.inner.lock();
290 
291             if inner.unfrozen.is_some() {
292                 return Err("time is not frozen");
293             }
294 
295             inner.base += duration;
296             Ok(())
297         }
298 
299         pub(crate) fn now(&self) -> Instant {
300             let inner = self.inner.lock();
301 
302             let mut ret = inner.base;
303 
304             if let Some(unfrozen) = inner.unfrozen {
305                 ret += unfrozen.elapsed();
306             }
307 
308             Instant::from_std(ret)
309         }
310     }
311 }
312