//! 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_test_util! { usecrate::time::{Duration, Instant}; usecrate::loom::sync::Mutex; usecrate::loom::sync::atomic::Ordering; use std::sync::atomic::AtomicBool as StdAtomicBool;
let res = match Handle::try_current() {
Ok(handle) => f(Some(handle.inner.driver().clock())),
Err(ref e) if e.is_missing_context() => f(None),
Err(_) => panic!("{}", crate::util::error::THREAD_LOCAL_DESTROYED_ERROR),
};
match res {
Ok(ret) => ret,
Err(msg) => panic!("{}", msg),
}
}
}
/// A handle to a source of time. #[derive(Debug)] pub(crate) struct Clock {
inner: Mutex<Inner>,
}
// Used to track if the clock was ever paused. This is an optimization to // avoid touching the mutex if `test-util` was accidentally enabled in // release mode. // // A static is used so we can avoid accessing the thread-local as well. The // `std` AtomicBool is used directly because loom does not support static // atomics. static DID_PAUSE_CLOCK: StdAtomicBool = StdAtomicBool::new(false);
#[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>,
/// Number of `inhibit_auto_advance` calls still in effect.
auto_advance_inhibit_count: usize,
}
/// Pauses time. /// /// The current value of `Instant::now()` is saved and all subsequent calls /// to `Instant::now()` will return the saved value. The saved value can be /// changed by [`advance`] or by the time auto-advancing once the runtime /// has no work to do. This only affects the `Instant` type in Tokio, and /// the `Instant` in std continues to work as normal. /// /// 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. /// /// For cases where time is immediately paused, it is better to pause /// the time using the `main` or `test` macro: /// ``` /// #[tokio::main(flavor = "current_thread", start_paused = true)] /// async fn main() { /// println!("Hello world"); /// } /// ``` /// /// # 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 /// [`advance`]: crate::time::advance #[track_caller] pubfn pause() {
with_clock(|maybe_clock| { match maybe_clock {
Some(clock) => clock.pause(),
None => Err("time cannot be frozen from outside the Tokio runtime"),
}
});
}
/// Resumes 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. #[track_caller] pubfn resume() {
with_clock(|maybe_clock| { let clock = match maybe_clock {
Some(clock) => clock,
None => return Err("time cannot be frozen from outside the Tokio runtime"),
};
letmut inner = clock.inner.lock();
if inner.unfrozen.is_some() { return Err("time is not frozen");
}
/// Advances time. /// /// Increments the saved `Instant::now()` value by `duration`. Subsequent /// calls to `Instant::now()` will return the result of the increment. /// /// This function will make the current time jump forward by the given /// duration in one jump. This means that all `sleep` calls with a deadline /// before the new time will immediately complete "at the same time", and /// the runtime is free to poll them in any order. Additionally, this /// method will not wait for the `sleep` calls it advanced past to complete. /// If you want to do that, you should instead call [`sleep`] and rely on /// the runtime's auto-advance feature. /// /// Note that calls to `sleep` are not guaranteed to complete the first time /// they are polled after a call to `advance`. For example, this can happen /// if the runtime has not yet touched the timer driver after the call to /// `advance`. However if they don't, the runtime will poll the task again /// shortly. /// /// # 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. /// /// [`sleep`]: fn@crate::time::sleep pubasyncfn advance(duration: Duration) {
with_clock(|maybe_clock| { let clock = match maybe_clock {
Some(clock) => clock,
None => return Err("time cannot be frozen from outside the Tokio runtime"),
};
clock.advance(duration)
});
crate::task::yield_now().await;
}
/// Returns the current instant, factoring in frozen time. pub(crate) fn now() -> Instant { if !DID_PAUSE_CLOCK.load(Ordering::Acquire) { return Instant::from_std(std::time::Instant::now());
}
impl Clock { /// Returns 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();
if !inner.enable_pausing {
drop(inner); // avoid poisoning the lock return Err("`time::pause()` requires the `current_thread` Tokio runtime. \
This is the default Runtime used by `#[tokio::test].");
}
// Track that we paused the clock
DID_PAUSE_CLOCK.store(true, Ordering::Release);
let elapsed = match inner.unfrozen.as_ref() {
Some(v) => v.elapsed(),
None => return Err("time is already frozen")
};
inner.base += elapsed;
inner.unfrozen = None;
Ok(())
}
/// Temporarily stop auto-advancing the clock (see `tokio::time::pause`). pub(crate) fn inhibit_auto_advance(&self) { letmut inner = self.inner.lock();
inner.auto_advance_inhibit_count += 1;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.