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 /// [`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 any of the following conditions are met:
189 ///
190 /// - The clock is not frozen, which means that you must
191 /// call [`pause`] before calling this method.
192 /// - If called outside of the Tokio runtime.
193 /// - If the input `duration` is too large (such as [`Duration::MAX`])
194 /// to be safely added to the current time without causing an overflow.
195 ///
196 /// # Caveats
197 ///
198 /// Using a very large `duration` is not recommended,
199 /// as it may cause panicking due to overflow.
200 ///
201 /// # Auto-advance
202 ///
203 /// If the time is paused and there is no work to do, the runtime advances
204 /// time to the next timer. See [`pause`](pause#auto-advance) for more
205 /// details.
206 ///
207 /// [`sleep`]: fn@crate::time::sleep
208 pub async fn advance(duration: Duration) {
209 with_clock(|maybe_clock| {
210 let clock = match maybe_clock {
211 Some(clock) => clock,
212 None => return Err("time cannot be frozen from outside the Tokio runtime"),
213 };
214
215 clock.advance(duration)
216 });
217
218 crate::task::yield_now().await;
219 }
220
221 /// Returns the current instant, factoring in frozen time.
222 pub(crate) fn now() -> Instant {
223 if !DID_PAUSE_CLOCK.load(Ordering::Acquire) {
224 return Instant::from_std(std::time::Instant::now());
225 }
226
227 with_clock(|maybe_clock| {
228 Ok(if let Some(clock) = maybe_clock {
229 clock.now()
230 } else {
231 Instant::from_std(std::time::Instant::now())
232 })
233 })
234 }
235
236 impl Clock {
237 /// Returns a new `Clock` instance that uses the current execution context's
238 /// source of time.
239 pub(crate) fn new(enable_pausing: bool, start_paused: bool) -> Clock {
240 let now = std::time::Instant::now();
241
242 let clock = Clock {
243 inner: Mutex::new(Inner {
244 enable_pausing,
245 base: now,
246 unfrozen: Some(now),
247 auto_advance_inhibit_count: 0,
248 }),
249 };
250
251 if start_paused {
252 if let Err(msg) = clock.pause() {
253 panic!("{}", msg);
254 }
255 }
256
257 clock
258 }
259
260 pub(crate) fn pause(&self) -> Result<(), &'static str> {
261 let mut inner = self.inner.lock();
262
263 if !inner.enable_pausing {
264 return Err("`time::pause()` requires the `current_thread` Tokio runtime. \
265 This is the default Runtime used by `#[tokio::test].");
266 }
267
268 // Track that we paused the clock
269 DID_PAUSE_CLOCK.store(true, Ordering::Release);
270
271 let elapsed = match inner.unfrozen.as_ref() {
272 Some(v) => v.elapsed(),
273 None => return Err("time is already frozen")
274 };
275 inner.base += elapsed;
276 inner.unfrozen = None;
277
278 Ok(())
279 }
280
281 /// Temporarily stop auto-advancing the clock (see `tokio::time::pause`).
282 pub(crate) fn inhibit_auto_advance(&self) {
283 let mut inner = self.inner.lock();
284 inner.auto_advance_inhibit_count += 1;
285 }
286
287 pub(crate) fn allow_auto_advance(&self) {
288 let mut inner = self.inner.lock();
289 inner.auto_advance_inhibit_count -= 1;
290 }
291
292 pub(crate) fn can_auto_advance(&self) -> bool {
293 let inner = self.inner.lock();
294 inner.unfrozen.is_none() && inner.auto_advance_inhibit_count == 0
295 }
296
297 pub(crate) fn advance(&self, duration: Duration) -> Result<(), &'static str> {
298 let mut inner = self.inner.lock();
299
300 if inner.unfrozen.is_some() {
301 return Err("time is not frozen");
302 }
303
304 inner.base += duration;
305 Ok(())
306 }
307
308 pub(crate) fn now(&self) -> Instant {
309 let inner = self.inner.lock();
310
311 let mut ret = inner.base;
312
313 if let Some(unfrozen) = inner.unfrozen {
314 ret += unfrozen.elapsed();
315 }
316
317 Instant::from_std(ret)
318 }
319 }
320}