Skip to main content

tokio/time/
clock.rs

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
9cfg_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
30cfg_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    /// # Preventing auto-advance
128    ///
129    /// In some testing scenarios, you may want to keep the clock paused without
130    /// auto-advancing, even while waiting for I/O or other asynchronous operations.
131    /// This can be achieved by using [`spawn_blocking`] to wrap your I/O operations.
132    ///
133    /// When a blocking task is running, the clock's auto-advance is temporarily
134    /// inhibited. This allows you to wait for I/O to complete while keeping the
135    /// paused clock stationary:
136    ///
137    /// ```ignore
138    /// use tokio::time::{Duration, Instant};
139    /// use tokio::task;
140    ///
141    /// #[tokio::test(start_paused = true)]
142    /// async fn test_with_io() {
143    ///     let start = Instant::now();
144    ///
145    ///     // The clock will NOT auto-advance while this blocking task runs
146    ///     let result = task::spawn_blocking(|| {
147    ///         // Perform I/O operations here
148    ///         std::thread::sleep(std::time::Duration::from_millis(10));
149    ///         42
150    ///     }).await.unwrap();
151    ///
152    ///     // Time has not advanced
153    ///     assert_eq!(start.elapsed(), Duration::ZERO);
154    /// }
155    /// ```
156    ///
157    /// [`Sleep`]: crate::time::Sleep
158    /// [`advance`]: crate::time::advance
159    /// [`spawn_blocking`]: crate::task::spawn_blocking
160    #[track_caller]
161    pub fn pause() {
162        with_clock(|maybe_clock| {
163            match maybe_clock {
164                Some(clock) => clock.pause(),
165                None => Err("time cannot be frozen from outside the Tokio runtime"),
166            }
167        });
168    }
169
170    /// Resumes time.
171    ///
172    /// Clears the saved `Instant::now()` value. Subsequent calls to
173    /// `Instant::now()` will return the value returned by the system call.
174    ///
175    /// # Panics
176    ///
177    /// Panics if time is not frozen or if called from outside of the Tokio
178    /// runtime.
179    #[track_caller]
180    pub fn resume() {
181        with_clock(|maybe_clock| {
182            let clock = match maybe_clock {
183                Some(clock) => clock,
184                None => return Err("time cannot be frozen from outside the Tokio runtime"),
185            };
186
187            let mut inner = clock.inner.lock();
188
189            if inner.unfrozen.is_some() {
190                return Err("time is not frozen");
191            }
192
193            inner.unfrozen = Some(std::time::Instant::now());
194            Ok(())
195        });
196    }
197
198    /// Advances time.
199    ///
200    /// Increments the saved `Instant::now()` value by `duration`. Subsequent
201    /// calls to `Instant::now()` will return the result of the increment.
202    ///
203    /// This function will make the current time jump forward by the given
204    /// duration in one jump. This means that all `sleep` calls with a deadline
205    /// before the new time will immediately complete "at the same time", and
206    /// the runtime is free to poll them in any order.  Additionally, this
207    /// method will not wait for the `sleep` calls it advanced past to complete.
208    /// If you want to do that, you should instead call [`sleep`] and rely on
209    /// the runtime's auto-advance feature.
210    ///
211    /// Note that calls to `sleep` are not guaranteed to complete the first time
212    /// they are polled after a call to `advance`. For example, this can happen
213    /// if the runtime has not yet touched the timer driver after the call to
214    /// `advance`. However if they don't, the runtime will poll the task again
215    /// shortly.
216    ///
217    /// # When to use `sleep` instead
218    ///
219    /// **Important:** `advance` is designed for testing scenarios where you want to
220    /// instantly jump forward in time. However, it has limitations that make it
221    /// unsuitable for certain use cases:
222    ///
223    /// - **Forcing timeouts:** If you want to reliably trigger a timeout, prefer
224    ///   using [`sleep`] with auto-advance rather than `advance`. The `advance`
225    ///   function jumps time forward but doesn't guarantee that all timers will be
226    ///   processed before your code continues.
227    ///
228    /// - **Simulating freezes:** If you're trying to simulate a scenario where the
229    ///   program freezes and then resumes, the batch behavior of `advance` may not
230    ///   produce the expected results. All timers that expire during the advance
231    ///   complete simultaneously.
232    ///
233    /// For most testing scenarios where you want to wait for a duration to pass
234    /// and have all timers fire in order, use [`sleep`] instead:
235    ///
236    /// ```ignore
237    /// use tokio::time::{self, Duration};
238    ///
239    /// #[tokio::test(start_paused = true)]
240    /// async fn test_timeout_reliable() {
241    ///     // Use sleep with auto-advance for reliable timeout testing
242    ///     time::sleep(Duration::from_secs(5)).await;
243    ///     // All timers that were scheduled to fire within 5 seconds
244    ///     // have now been processed in order
245    /// }
246    /// ```
247    ///
248    /// # Panics
249    ///
250    /// Panics if any of the following conditions are met:
251    ///
252    /// - The clock is not frozen, which means that you must
253    ///   call [`pause`] before calling this method.
254    /// - If called outside of the Tokio runtime.
255    /// - If the input `duration` is too large (such as [`Duration::MAX`])
256    ///   to be safely added to the current time without causing an overflow.
257    ///
258    /// # Caveats
259    ///
260    /// Using a very large `duration` is not recommended,
261    /// as it may cause panicking due to overflow.
262    ///
263    /// # Auto-advance
264    ///
265    /// If the time is paused and there is no work to do, the runtime advances
266    /// time to the next timer. See [`pause`](pause#auto-advance) for more
267    /// details.
268    ///
269    /// [`sleep`]: fn@crate::time::sleep
270    pub async fn advance(duration: Duration) {
271        with_clock(|maybe_clock| {
272            let clock = match maybe_clock {
273                Some(clock) => clock,
274                None => return Err("time cannot be frozen from outside the Tokio runtime"),
275            };
276
277            clock.advance(duration)
278        });
279
280        crate::task::yield_now().await;
281    }
282
283    /// Returns the current instant, factoring in frozen time.
284    pub(crate) fn now() -> Instant {
285        if !DID_PAUSE_CLOCK.load(Ordering::Acquire) {
286            return Instant::from_std(std::time::Instant::now());
287        }
288
289        with_clock(|maybe_clock| {
290            Ok(if let Some(clock) = maybe_clock {
291                clock.now()
292            } else {
293                Instant::from_std(std::time::Instant::now())
294            })
295        })
296    }
297
298    impl Clock {
299        /// Returns a new `Clock` instance that uses the current execution context's
300        /// source of time.
301        pub(crate) fn new(enable_pausing: bool, start_paused: bool) -> Clock {
302            let now = std::time::Instant::now();
303
304            let clock = Clock {
305                inner: Mutex::new(Inner {
306                    enable_pausing,
307                    base: now,
308                    unfrozen: Some(now),
309                    auto_advance_inhibit_count: 0,
310                }),
311            };
312
313            if start_paused {
314                if let Err(msg) = clock.pause() {
315                    panic!("{}", msg);
316                }
317            }
318
319            clock
320        }
321
322        pub(crate) fn pause(&self) -> Result<(), &'static str> {
323            let mut inner = self.inner.lock();
324
325            if !inner.enable_pausing {
326                return Err("`time::pause()` requires the `current_thread` Tokio runtime. \
327                        This is the default Runtime used by `#[tokio::test].");
328            }
329
330            // Track that we paused the clock
331            DID_PAUSE_CLOCK.store(true, Ordering::Release);
332
333            let elapsed = match inner.unfrozen.as_ref() {
334                Some(v) => v.elapsed(),
335                None => return Err("time is already frozen")
336            };
337            inner.base += elapsed;
338            inner.unfrozen = None;
339
340            Ok(())
341        }
342
343        /// Temporarily stop auto-advancing the clock (see `tokio::time::pause`).
344        pub(crate) fn inhibit_auto_advance(&self) {
345            let mut inner = self.inner.lock();
346            inner.auto_advance_inhibit_count += 1;
347        }
348
349        pub(crate) fn allow_auto_advance(&self) {
350            let mut inner = self.inner.lock();
351            inner.auto_advance_inhibit_count -= 1;
352        }
353
354        pub(crate) fn can_auto_advance(&self) -> bool {
355            let inner = self.inner.lock();
356            inner.unfrozen.is_none() && inner.auto_advance_inhibit_count == 0
357        }
358
359        pub(crate) fn advance(&self, duration: Duration) -> Result<(), &'static str> {
360            let mut inner = self.inner.lock();
361
362            if inner.unfrozen.is_some() {
363                return Err("time is not frozen");
364            }
365
366            inner.base += duration;
367            Ok(())
368        }
369
370        pub(crate) fn now(&self) -> Instant {
371            let inner = self.inner.lock();
372
373            let mut ret = inner.base;
374
375            if let Some(unfrozen) = inner.unfrozen {
376                ret += unfrozen.elapsed();
377            }
378
379            Instant::from_std(ret)
380        }
381    }
382}