onetaskgraph_plugin_api/clock.rs
1//! The clock a source paces and backs off on.
2//!
3//! A plugin that waits — between pages to stay under a published rate, or before retrying a
4//! refused request — waits on the [`Clock`] it was built with rather than on the runtime's own
5//! timer, so a test can run it on simulated time: a minute of pacing then costs no real time,
6//! and the figures a timing budget reports are the same on every run. The binary hands every
7//! in-process source the process's one clock through [`SourcePlugin::build_with_clock`]; with
8//! nothing asking for simulated time that is [`system_clock`], and every plugin behaves exactly
9//! as it does on the runtime's timer.
10//!
11//! [`SourcePlugin::build_with_clock`]: crate::SourcePlugin::build_with_clock
12
13use std::future::Future;
14use std::pin::Pin;
15use std::sync::Arc;
16use std::time::{Duration, Instant};
17
18/// A source of monotonic time and of waits measured against it.
19pub trait Clock: Send + Sync {
20 /// Monotonic time elapsed since this clock's origin.
21 fn now(&self) -> std::time::Duration;
22 /// Complete once `duration` has passed on this clock.
23 fn sleep(
24 &self,
25 duration: std::time::Duration,
26 ) -> std::pin::Pin<Box<dyn std::future::Future<Output = ()> + Send + 'static>>;
27}
28
29/// The variable that puts a process of this build on a simulated clock.
30///
31/// Its value is `<address>/<client>`: a test's coordinator's loopback address and the
32/// process's 0-based client number. Unset or empty, the process runs on [`system_clock`]. It
33/// does not begin `ONETASKGRAPH_`, so the configuration's environment layer never reads it as a
34/// setting. Named here, beside the clock it selects, so the binary that reads it and the test
35/// harness that hands it out spell it once between them.
36pub const SIMULATED_CLOCK_VARIABLE: &str = "OTG_SIMULATED_CLOCK";
37
38/// One clock, shared by everything a process builds.
39pub type SharedClock = std::sync::Arc<dyn Clock>;
40
41/// The real clock: `tokio::time::sleep` and a monotonic `Instant`.
42#[must_use]
43pub fn system_clock() -> SharedClock {
44 Arc::new(SystemClock {
45 origin: Instant::now(),
46 })
47}
48
49/// [`system_clock`]'s clock: its origin is the instant it was made.
50struct SystemClock {
51 origin: Instant,
52}
53
54impl Clock for SystemClock {
55 fn now(&self) -> Duration {
56 self.origin.elapsed()
57 }
58
59 fn sleep(&self, duration: Duration) -> Pin<Box<dyn Future<Output = ()> + Send + 'static>> {
60 Box::pin(tokio::time::sleep(duration))
61 }
62}