Coverage Report

Created: 2025-08-29 06:13

/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
}