| #![cfg_attr(not(feature = "rt"), allow(dead_code))] |
| |
| //! Source of time abstraction. |
| //! |
| //! By default, `std::time::Instant::now()` is used. However, when the |
| //! `test-util` feature flag is enabled, the values returned for `now()` are |
| //! configurable. |
| |
| cfg_not_test_util! { |
| use crate::time::{Instant}; |
| |
| #[derive(Debug, Clone)] |
| pub(crate) struct Clock {} |
| |
| pub(crate) fn now() -> Instant { |
| Instant::from_std(std::time::Instant::now()) |
| } |
| |
| impl Clock { |
| pub(crate) fn new(_enable_pausing: bool, _start_paused: bool) -> Clock { |
| Clock {} |
| } |
| |
| pub(crate) fn now(&self) -> Instant { |
| now() |
| } |
| } |
| } |
| |
| cfg_test_util! { |
| use crate::time::{Duration, Instant}; |
| use crate::loom::sync::{Arc, Mutex}; |
| |
| cfg_rt! { |
| fn clock() -> Option<Clock> { |
| crate::runtime::context::clock() |
| } |
| } |
| |
| cfg_not_rt! { |
| fn clock() -> Option<Clock> { |
| None |
| } |
| } |
| |
| /// A handle to a source of time. |
| #[derive(Debug, Clone)] |
| pub(crate) struct Clock { |
| inner: Arc<Mutex<Inner>>, |
| } |
| |
| #[derive(Debug)] |
| struct Inner { |
| /// True if the ability to pause time is enabled. |
| enable_pausing: bool, |
| |
| /// Instant to use as the clock's base instant. |
| base: std::time::Instant, |
| |
| /// Instant at which the clock was last unfrozen |
| unfrozen: Option<std::time::Instant>, |
| } |
| |
| /// Pause time |
| /// |
| /// The current value of `Instant::now()` is saved and all subsequent calls |
| /// to `Instant::now()` until the timer wheel is checked again will return |
| /// the saved value. Once the timer wheel is checked, time will immediately |
| /// advance to the next registered `Sleep`. This is useful for running tests |
| /// that depend on time. |
| /// |
| /// Pausing time requires the `current_thread` Tokio runtime. This is the |
| /// default runtime used by `#[tokio::test]`. The runtime can be initialized |
| /// with time in a paused state using the `Builder::start_paused` method. |
| /// |
| /// # Panics |
| /// |
| /// Panics if time is already frozen or if called from outside of a |
| /// `current_thread` Tokio runtime. |
| /// |
| /// # Auto-advance |
| /// |
| /// If time is paused and the runtime has no work to do, the clock is |
| /// auto-advanced to the next pending timer. This means that [`Sleep`] or |
| /// other timer-backed primitives can cause the runtime to advance the |
| /// current time when awaited. |
| /// |
| /// [`Sleep`]: crate::time::Sleep |
| pub fn pause() { |
| let clock = clock().expect("time cannot be frozen from outside the Tokio runtime"); |
| clock.pause(); |
| } |
| |
| /// Resume time |
| /// |
| /// Clears the saved `Instant::now()` value. Subsequent calls to |
| /// `Instant::now()` will return the value returned by the system call. |
| /// |
| /// # Panics |
| /// |
| /// Panics if time is not frozen or if called from outside of the Tokio |
| /// runtime. |
| pub fn resume() { |
| let clock = clock().expect("time cannot be frozen from outside the Tokio runtime"); |
| let mut inner = clock.inner.lock(); |
| |
| if inner.unfrozen.is_some() { |
| panic!("time is not frozen"); |
| } |
| |
| inner.unfrozen = Some(std::time::Instant::now()); |
| } |
| |
| /// Advance time |
| /// |
| /// Increments the saved `Instant::now()` value by `duration`. Subsequent |
| /// calls to `Instant::now()` will return the result of the increment. |
| /// |
| /// # Panics |
| /// |
| /// Panics if time is not frozen or if called from outside of the Tokio |
| /// runtime. |
| /// |
| /// # Auto-advance |
| /// |
| /// If the time is paused and there is no work to do, the runtime advances |
| /// time to the next timer. See [`pause`](pause#auto-advance) for more |
| /// details. |
| pub async fn advance(duration: Duration) { |
| let clock = clock().expect("time cannot be frozen from outside the Tokio runtime"); |
| clock.advance(duration); |
| |
| crate::task::yield_now().await; |
| } |
| |
| /// Return the current instant, factoring in frozen time. |
| pub(crate) fn now() -> Instant { |
| if let Some(clock) = clock() { |
| clock.now() |
| } else { |
| Instant::from_std(std::time::Instant::now()) |
| } |
| } |
| |
| impl Clock { |
| /// Return a new `Clock` instance that uses the current execution context's |
| /// source of time. |
| pub(crate) fn new(enable_pausing: bool, start_paused: bool) -> Clock { |
| let now = std::time::Instant::now(); |
| |
| let clock = Clock { |
| inner: Arc::new(Mutex::new(Inner { |
| enable_pausing, |
| base: now, |
| unfrozen: Some(now), |
| })), |
| }; |
| |
| if start_paused { |
| clock.pause(); |
| } |
| |
| clock |
| } |
| |
| pub(crate) fn pause(&self) { |
| let mut inner = self.inner.lock(); |
| |
| if !inner.enable_pausing { |
| drop(inner); // avoid poisoning the lock |
| panic!("`time::pause()` requires the `current_thread` Tokio runtime. \ |
| This is the default Runtime used by `#[tokio::test]."); |
| } |
| |
| let elapsed = inner.unfrozen.as_ref().expect("time is already frozen").elapsed(); |
| inner.base += elapsed; |
| inner.unfrozen = None; |
| } |
| |
| pub(crate) fn is_paused(&self) -> bool { |
| let inner = self.inner.lock(); |
| inner.unfrozen.is_none() |
| } |
| |
| pub(crate) fn advance(&self, duration: Duration) { |
| let mut inner = self.inner.lock(); |
| |
| if inner.unfrozen.is_some() { |
| panic!("time is not frozen"); |
| } |
| |
| inner.base += duration; |
| } |
| |
| pub(crate) fn now(&self) -> Instant { |
| let inner = self.inner.lock(); |
| |
| let mut ret = inner.base; |
| |
| if let Some(unfrozen) = inner.unfrozen { |
| ret += unfrozen.elapsed(); |
| } |
| |
| Instant::from_std(ret) |
| } |
| } |
| } |