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