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::{Arc, Mutex};
33 
34     cfg_rt! {
35         fn clock() -> Option<Clock> {
36             crate::runtime::context::clock()
37         }
38     }
39 
40     cfg_not_rt! {
41         fn clock() -> Option<Clock> {
42             None
43         }
44     }
45 
46     /// A handle to a source of time.
47     #[derive(Debug, Clone)]
48     pub(crate) struct Clock {
49         inner: Arc<Mutex<Inner>>,
50     }
51 
52     #[derive(Debug)]
53     struct Inner {
54         /// True if the ability to pause time is enabled.
55         enable_pausing: bool,
56 
57         /// Instant to use as the clock's base instant.
58         base: std::time::Instant,
59 
60         /// Instant at which the clock was last unfrozen
61         unfrozen: Option<std::time::Instant>,
62     }
63 
64     /// Pause time
65     ///
66     /// The current value of `Instant::now()` is saved and all subsequent calls
67     /// to `Instant::now()` until the timer wheel is checked again will return
68     /// the saved value. Once the timer wheel is checked, time will immediately
69     /// advance to the next registered `Sleep`. This is useful for running tests
70     /// that depend on time.
71     ///
72     /// Pausing time requires the `current_thread` Tokio runtime. This is the
73     /// default runtime used by `#[tokio::test]`. The runtime can be initialized
74     /// with time in a paused state using the `Builder::start_paused` method.
75     ///
76     /// # Panics
77     ///
78     /// Panics if time is already frozen or if called from outside of a
79     /// `current_thread` Tokio runtime.
80     ///
81     /// # Auto-advance
82     ///
83     /// If time is paused and the runtime has no work to do, the clock is
84     /// auto-advanced to the next pending timer. This means that [`Sleep`] or
85     /// other timer-backed primitives can cause the runtime to advance the
86     /// current time when awaited.
87     ///
88     /// [`Sleep`]: crate::time::Sleep
89     pub fn pause() {
90         let clock = clock().expect("time cannot be frozen from outside the Tokio runtime");
91         clock.pause();
92     }
93 
94     /// Resume time
95     ///
96     /// Clears the saved `Instant::now()` value. Subsequent calls to
97     /// `Instant::now()` will return the value returned by the system call.
98     ///
99     /// # Panics
100     ///
101     /// Panics if time is not frozen or if called from outside of the Tokio
102     /// runtime.
103     pub fn resume() {
104         let clock = clock().expect("time cannot be frozen from outside the Tokio runtime");
105         let mut inner = clock.inner.lock();
106 
107         if inner.unfrozen.is_some() {
108             panic!("time is not frozen");
109         }
110 
111         inner.unfrozen = Some(std::time::Instant::now());
112     }
113 
114     /// Advance time
115     ///
116     /// Increments the saved `Instant::now()` value by `duration`. Subsequent
117     /// calls to `Instant::now()` will return the result of the increment.
118     ///
119     /// # Panics
120     ///
121     /// Panics if time is not frozen or if called from outside of the Tokio
122     /// runtime.
123     ///
124     /// # Auto-advance
125     ///
126     /// If the time is paused and there is no work to do, the runtime advances
127     /// time to the next timer. See [`pause`](pause#auto-advance) for more
128     /// details.
129     pub async fn advance(duration: Duration) {
130         let clock = clock().expect("time cannot be frozen from outside the Tokio runtime");
131         clock.advance(duration);
132 
133         crate::task::yield_now().await;
134     }
135 
136     /// Return the current instant, factoring in frozen time.
137     pub(crate) fn now() -> Instant {
138         if let Some(clock) = clock() {
139             clock.now()
140         } else {
141             Instant::from_std(std::time::Instant::now())
142         }
143     }
144 
145     impl Clock {
146         /// Return a new `Clock` instance that uses the current execution context's
147         /// source of time.
148         pub(crate) fn new(enable_pausing: bool, start_paused: bool) -> Clock {
149             let now = std::time::Instant::now();
150 
151             let clock = Clock {
152                 inner: Arc::new(Mutex::new(Inner {
153                     enable_pausing,
154                     base: now,
155                     unfrozen: Some(now),
156                 })),
157             };
158 
159             if start_paused {
160                 clock.pause();
161             }
162 
163             clock
164         }
165 
166         pub(crate) fn pause(&self) {
167             let mut inner = self.inner.lock();
168 
169             if !inner.enable_pausing {
170                 drop(inner); // avoid poisoning the lock
171                 panic!("`time::pause()` requires the `current_thread` Tokio runtime. \
172                         This is the default Runtime used by `#[tokio::test].");
173             }
174 
175             let elapsed = inner.unfrozen.as_ref().expect("time is already frozen").elapsed();
176             inner.base += elapsed;
177             inner.unfrozen = None;
178         }
179 
180         pub(crate) fn is_paused(&self) -> bool {
181             let inner = self.inner.lock();
182             inner.unfrozen.is_none()
183         }
184 
185         pub(crate) fn advance(&self, duration: Duration) {
186             let mut inner = self.inner.lock();
187 
188             if inner.unfrozen.is_some() {
189                 panic!("time is not frozen");
190             }
191 
192             inner.base += duration;
193         }
194 
195         pub(crate) fn now(&self) -> Instant {
196             let inner = self.inner.lock();
197 
198             let mut ret = inner.base;
199 
200             if let Some(unfrozen) = inner.unfrozen {
201                 ret += unfrozen.elapsed();
202             }
203 
204             Instant::from_std(ret)
205         }
206     }
207 }
208