/rust/registry/src/index.crates.io-1949cf8c6b5b557f/tokio-1.52.3/src/time/clock.rs
Line | Count | Source |
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 | 0 | fn with_clock<R>(f: impl FnOnce(Option<&Clock>) -> Result<R, &'static str>) -> R { |
39 | | use crate::runtime::Handle; |
40 | | |
41 | 0 | let res = match Handle::try_current() { |
42 | 0 | Ok(handle) => f(Some(handle.inner.driver().clock())), |
43 | 0 | Err(ref e) if e.is_missing_context() => f(None), |
44 | 0 | Err(_) => panic!("{}", crate::util::error::THREAD_LOCAL_DESTROYED_ERROR), |
45 | | }; |
46 | | |
47 | 0 | match res { |
48 | 0 | Ok(ret) => ret, |
49 | 0 | Err(msg) => panic!("{}", msg), |
50 | | } |
51 | 0 | } Unexecuted instantiation: tokio::time::clock::with_clock::<tokio::time::instant::Instant, tokio::time::clock::now::{closure#0}>Unexecuted instantiation: tokio::time::clock::with_clock::<(), tokio::time::clock::advance::{closure#0}::{closure#0}>Unexecuted instantiation: tokio::time::clock::with_clock::<(), tokio::time::clock::pause::{closure#0}>Unexecuted instantiation: tokio::time::clock::with_clock::<(), tokio::time::clock::resume::{closure#0}> |
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 | 0 | pub fn pause() { |
162 | 0 | with_clock(|maybe_clock| { |
163 | 0 | match maybe_clock { |
164 | 0 | Some(clock) => clock.pause(), |
165 | 0 | None => Err("time cannot be frozen from outside the Tokio runtime"), |
166 | | } |
167 | 0 | }); |
168 | 0 | } |
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 | 0 | pub fn resume() { |
181 | 0 | with_clock(|maybe_clock| { |
182 | 0 | let clock = match maybe_clock { |
183 | 0 | Some(clock) => clock, |
184 | 0 | None => return Err("time cannot be frozen from outside the Tokio runtime"), |
185 | | }; |
186 | | |
187 | 0 | let mut inner = clock.inner.lock(); |
188 | | |
189 | 0 | if inner.unfrozen.is_some() { |
190 | 0 | return Err("time is not frozen"); |
191 | 0 | } |
192 | | |
193 | 0 | inner.unfrozen = Some(std::time::Instant::now()); |
194 | 0 | Ok(()) |
195 | 0 | }); |
196 | 0 | } |
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 | 0 | pub async fn advance(duration: Duration) { |
271 | 0 | with_clock(|maybe_clock| { |
272 | 0 | let clock = match maybe_clock { |
273 | 0 | Some(clock) => clock, |
274 | 0 | None => return Err("time cannot be frozen from outside the Tokio runtime"), |
275 | | }; |
276 | | |
277 | 0 | clock.advance(duration) |
278 | 0 | }); |
279 | | |
280 | 0 | crate::task::yield_now().await; |
281 | 0 | } |
282 | | |
283 | | /// Returns the current instant, factoring in frozen time. |
284 | 0 | pub(crate) fn now() -> Instant { |
285 | 0 | if !DID_PAUSE_CLOCK.load(Ordering::Acquire) { |
286 | 0 | return Instant::from_std(std::time::Instant::now()); |
287 | 0 | } |
288 | | |
289 | 0 | with_clock(|maybe_clock| { |
290 | 0 | Ok(if let Some(clock) = maybe_clock { |
291 | 0 | clock.now() |
292 | | } else { |
293 | 0 | Instant::from_std(std::time::Instant::now()) |
294 | | }) |
295 | 0 | }) |
296 | 0 | } |
297 | | |
298 | | impl Clock { |
299 | | /// Returns a new `Clock` instance that uses the current execution context's |
300 | | /// source of time. |
301 | 19.0k | pub(crate) fn new(enable_pausing: bool, start_paused: bool) -> Clock { |
302 | 19.0k | let now = std::time::Instant::now(); |
303 | | |
304 | 19.0k | let clock = Clock { |
305 | 19.0k | inner: Mutex::new(Inner { |
306 | 19.0k | enable_pausing, |
307 | 19.0k | base: now, |
308 | 19.0k | unfrozen: Some(now), |
309 | 19.0k | auto_advance_inhibit_count: 0, |
310 | 19.0k | }), |
311 | 19.0k | }; |
312 | | |
313 | 19.0k | if start_paused { |
314 | 0 | if let Err(msg) = clock.pause() { |
315 | 0 | panic!("{}", msg); |
316 | 0 | } |
317 | 19.0k | } |
318 | | |
319 | 19.0k | clock |
320 | 19.0k | } |
321 | | |
322 | 0 | pub(crate) fn pause(&self) -> Result<(), &'static str> { |
323 | 0 | let mut inner = self.inner.lock(); |
324 | | |
325 | 0 | if !inner.enable_pausing { |
326 | 0 | return Err("`time::pause()` requires the `current_thread` Tokio runtime. \ |
327 | 0 | This is the default Runtime used by `#[tokio::test]."); |
328 | 0 | } |
329 | | |
330 | | // Track that we paused the clock |
331 | 0 | DID_PAUSE_CLOCK.store(true, Ordering::Release); |
332 | | |
333 | 0 | let elapsed = match inner.unfrozen.as_ref() { |
334 | 0 | Some(v) => v.elapsed(), |
335 | 0 | None => return Err("time is already frozen") |
336 | | }; |
337 | 0 | inner.base += elapsed; |
338 | 0 | inner.unfrozen = None; |
339 | | |
340 | 0 | Ok(()) |
341 | 0 | } |
342 | | |
343 | | /// Temporarily stop auto-advancing the clock (see `tokio::time::pause`). |
344 | 0 | pub(crate) fn inhibit_auto_advance(&self) { |
345 | 0 | let mut inner = self.inner.lock(); |
346 | 0 | inner.auto_advance_inhibit_count += 1; |
347 | 0 | } |
348 | | |
349 | 0 | pub(crate) fn allow_auto_advance(&self) { |
350 | 0 | let mut inner = self.inner.lock(); |
351 | 0 | inner.auto_advance_inhibit_count -= 1; |
352 | 0 | } |
353 | | |
354 | 0 | pub(crate) fn can_auto_advance(&self) -> bool { |
355 | 0 | let inner = self.inner.lock(); |
356 | 0 | inner.unfrozen.is_none() && inner.auto_advance_inhibit_count == 0 |
357 | 0 | } |
358 | | |
359 | 0 | pub(crate) fn advance(&self, duration: Duration) -> Result<(), &'static str> { |
360 | 0 | let mut inner = self.inner.lock(); |
361 | | |
362 | 0 | if inner.unfrozen.is_some() { |
363 | 0 | return Err("time is not frozen"); |
364 | 0 | } |
365 | | |
366 | 0 | inner.base += duration; |
367 | 0 | Ok(()) |
368 | 0 | } |
369 | | |
370 | 42.5k | pub(crate) fn now(&self) -> Instant { |
371 | 42.5k | let inner = self.inner.lock(); |
372 | | |
373 | 42.5k | let mut ret = inner.base; |
374 | | |
375 | 42.5k | if let Some(unfrozen) = inner.unfrozen { |
376 | 42.5k | ret += unfrozen.elapsed(); |
377 | 42.5k | } |
378 | | |
379 | 42.5k | Instant::from_std(ret) |
380 | 42.5k | } |
381 | | } |
382 | | } |