/rust/registry/src/index.crates.io-6f17d22bba15001f/tokio-1.47.1/src/time/clock.rs
Line | Count | Source (jump to first uncovered line) |
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::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 | | /// [`Sleep`]: crate::time::Sleep |
128 | | /// [`advance`]: crate::time::advance |
129 | | #[track_caller] |
130 | 0 | pub fn pause() { |
131 | 0 | with_clock(|maybe_clock| { |
132 | 0 | match maybe_clock { |
133 | 0 | Some(clock) => clock.pause(), |
134 | 0 | None => Err("time cannot be frozen from outside the Tokio runtime"), |
135 | | } |
136 | 0 | }); |
137 | 0 | } |
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 | 0 | pub fn resume() { |
150 | 0 | with_clock(|maybe_clock| { |
151 | 0 | let clock = match maybe_clock { |
152 | 0 | Some(clock) => clock, |
153 | 0 | None => return Err("time cannot be frozen from outside the Tokio runtime"), |
154 | | }; |
155 | | |
156 | 0 | let mut inner = clock.inner.lock(); |
157 | 0 |
|
158 | 0 | if inner.unfrozen.is_some() { |
159 | 0 | return Err("time is not frozen"); |
160 | 0 | } |
161 | 0 |
|
162 | 0 | inner.unfrozen = Some(std::time::Instant::now()); |
163 | 0 | Ok(()) |
164 | 0 | }); |
165 | 0 | } |
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 | 0 | pub async fn advance(duration: Duration) { |
209 | 0 | with_clock(|maybe_clock| { |
210 | 0 | let clock = match maybe_clock { |
211 | 0 | Some(clock) => clock, |
212 | 0 | None => return Err("time cannot be frozen from outside the Tokio runtime"), |
213 | | }; |
214 | | |
215 | 0 | clock.advance(duration) |
216 | 0 | }); |
217 | 0 |
|
218 | 0 | crate::task::yield_now().await; |
219 | 0 | } |
220 | | |
221 | | /// Returns the current instant, factoring in frozen time. |
222 | 0 | pub(crate) fn now() -> Instant { |
223 | 0 | if !DID_PAUSE_CLOCK.load(Ordering::Acquire) { |
224 | 0 | return Instant::from_std(std::time::Instant::now()); |
225 | 0 | } |
226 | 0 |
|
227 | 0 | with_clock(|maybe_clock| { |
228 | 0 | Ok(if let Some(clock) = maybe_clock { |
229 | 0 | clock.now() |
230 | | } else { |
231 | 0 | Instant::from_std(std::time::Instant::now()) |
232 | | }) |
233 | 0 | }) |
234 | 0 | } |
235 | | |
236 | | impl Clock { |
237 | | /// Returns a new `Clock` instance that uses the current execution context's |
238 | | /// source of time. |
239 | 16.9k | pub(crate) fn new(enable_pausing: bool, start_paused: bool) -> Clock { |
240 | 16.9k | let now = std::time::Instant::now(); |
241 | 16.9k | |
242 | 16.9k | let clock = Clock { |
243 | 16.9k | inner: Mutex::new(Inner { |
244 | 16.9k | enable_pausing, |
245 | 16.9k | base: now, |
246 | 16.9k | unfrozen: Some(now), |
247 | 16.9k | auto_advance_inhibit_count: 0, |
248 | 16.9k | }), |
249 | 16.9k | }; |
250 | 16.9k | |
251 | 16.9k | if start_paused { |
252 | 0 | if let Err(msg) = clock.pause() { |
253 | 0 | panic!("{}", msg); |
254 | 0 | } |
255 | 16.9k | } |
256 | | |
257 | 16.9k | clock |
258 | 16.9k | } |
259 | | |
260 | 0 | pub(crate) fn pause(&self) -> Result<(), &'static str> { |
261 | 0 | let mut inner = self.inner.lock(); |
262 | 0 |
|
263 | 0 | if !inner.enable_pausing { |
264 | 0 | return Err("`time::pause()` requires the `current_thread` Tokio runtime. \ |
265 | 0 | This is the default Runtime used by `#[tokio::test]."); |
266 | 0 | } |
267 | 0 |
|
268 | 0 | // Track that we paused the clock |
269 | 0 | DID_PAUSE_CLOCK.store(true, Ordering::Release); |
270 | | |
271 | 0 | let elapsed = match inner.unfrozen.as_ref() { |
272 | 0 | Some(v) => v.elapsed(), |
273 | 0 | None => return Err("time is already frozen") |
274 | | }; |
275 | 0 | inner.base += elapsed; |
276 | 0 | inner.unfrozen = None; |
277 | 0 |
|
278 | 0 | Ok(()) |
279 | 0 | } |
280 | | |
281 | | /// Temporarily stop auto-advancing the clock (see `tokio::time::pause`). |
282 | 0 | pub(crate) fn inhibit_auto_advance(&self) { |
283 | 0 | let mut inner = self.inner.lock(); |
284 | 0 | inner.auto_advance_inhibit_count += 1; |
285 | 0 | } |
286 | | |
287 | 0 | pub(crate) fn allow_auto_advance(&self) { |
288 | 0 | let mut inner = self.inner.lock(); |
289 | 0 | inner.auto_advance_inhibit_count -= 1; |
290 | 0 | } |
291 | | |
292 | 0 | pub(crate) fn can_auto_advance(&self) -> bool { |
293 | 0 | let inner = self.inner.lock(); |
294 | 0 | inner.unfrozen.is_none() && inner.auto_advance_inhibit_count == 0 |
295 | 0 | } |
296 | | |
297 | 0 | pub(crate) fn advance(&self, duration: Duration) -> Result<(), &'static str> { |
298 | 0 | let mut inner = self.inner.lock(); |
299 | 0 |
|
300 | 0 | if inner.unfrozen.is_some() { |
301 | 0 | return Err("time is not frozen"); |
302 | 0 | } |
303 | 0 |
|
304 | 0 | inner.base += duration; |
305 | 0 | Ok(()) |
306 | 0 | } |
307 | | |
308 | 37.4k | pub(crate) fn now(&self) -> Instant { |
309 | 37.4k | let inner = self.inner.lock(); |
310 | 37.4k | |
311 | 37.4k | let mut ret = inner.base; |
312 | | |
313 | 37.4k | if let Some(unfrozen) = inner.unfrozen { |
314 | 37.4k | ret += unfrozen.elapsed(); |
315 | 37.4k | } |
316 | | |
317 | 37.4k | Instant::from_std(ret) |
318 | 37.4k | } |
319 | | } |
320 | | } |