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