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}