Skip to main content

lash_core/runtime/
clock.rs

1//! Injected time source.
2//!
3//! lash is a replayable durable runtime, so time is an effect like any other:
4//! durable timestamps must be reproducible under replay, and timeout/backoff
5//! logic must be drivable deterministically in tests. Every wall-clock and
6//! monotonic read in the runtime path goes through a [`Clock`]; the default
7//! [`SystemClock`] reads the real OS clock, and tests inject a controllable
8//! one.
9//!
10//! Boundary: `now()` is monotonic (measurement/elapsed only, never persisted);
11//! `timestamp_ms`/`timestamp_rfc3339` are wall-clock (durable records). `sleep`
12//! and `sleep_until` replace direct `tokio::time` calls so a fake clock can
13//! resolve them without real wall-clock waits.
14
15use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
16
17use async_trait::async_trait;
18
19/// Runtime time source. Cloneable as `Arc<dyn Clock>`; carried on
20/// [`RuntimeHostConfig`](super::RuntimeHostConfig).
21#[async_trait]
22pub trait Clock: Send + Sync + std::fmt::Debug {
23    /// Monotonic instant for measuring elapsed time. Never persisted.
24    fn now(&self) -> Instant;
25
26    /// Wall-clock time as epoch milliseconds, for durable records.
27    fn timestamp_ms(&self) -> u64;
28
29    /// Wall-clock time as an RFC 3339 string, for durable records.
30    fn timestamp_rfc3339(&self) -> String;
31
32    /// Wall-clock time as a UTC datetime, for trace records.
33    fn timestamp_datetime(&self) -> chrono::DateTime<chrono::Utc>;
34
35    /// Sleep for `duration`. Replaces `tokio::time::sleep`.
36    async fn sleep(&self, duration: Duration);
37
38    /// Sleep until `deadline` (a value from [`now`](Clock::now)). Replaces
39    /// `tokio::time::sleep_until`.
40    async fn sleep_until(&self, deadline: Instant);
41}
42
43/// The real OS clock. Native behavior is identical to the direct `std`/`tokio`
44/// calls it replaces.
45#[derive(Debug, Default, Clone, Copy)]
46pub struct SystemClock;
47
48#[async_trait]
49impl Clock for SystemClock {
50    fn now(&self) -> Instant {
51        Instant::now()
52    }
53
54    fn timestamp_ms(&self) -> u64 {
55        SystemTime::now()
56            .duration_since(UNIX_EPOCH)
57            .unwrap_or_default()
58            .as_millis() as u64
59    }
60
61    fn timestamp_rfc3339(&self) -> String {
62        self.timestamp_datetime().to_rfc3339()
63    }
64
65    fn timestamp_datetime(&self) -> chrono::DateTime<chrono::Utc> {
66        chrono::Utc::now()
67    }
68
69    async fn sleep(&self, duration: Duration) {
70        tokio::time::sleep(duration).await;
71    }
72
73    async fn sleep_until(&self, deadline: Instant) {
74        tokio::time::sleep_until(tokio::time::Instant::from_std(deadline)).await;
75    }
76}