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}