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