1use std::fmt;
4use std::sync::{Arc, OnceLock};
5use std::time::{Duration, Instant, SystemTime, UNIX_EPOCH};
6
7pub trait Clock: Send + Sync + 'static {
12 fn wall_ms(&self) -> i64;
14 fn elapsed(&self) -> Duration;
16 fn elapsed_ms(&self) -> i64 {
21 self.elapsed().as_millis().min(i64::MAX as u128) as i64
22 }
23}
24
25static PROCESS_ORIGIN: OnceLock<Instant> = OnceLock::new();
26
27pub fn grid_origin_ns(now_ns: u128) -> u128 {
31 now_ns - now_ns % 1_000_000
32}
33
34type MonotonicSource = Arc<dyn Fn() -> Duration + Send + Sync>;
35type WallSource = Arc<dyn Fn() -> i64 + Send + Sync>;
36
37#[derive(Clone)]
40pub struct SystemClock {
41 monotonic: MonotonicSource,
42 wall: WallSource,
43 origin_ns: u128,
45}
46
47impl SystemClock {
48 pub fn new() -> Self {
51 let process = *PROCESS_ORIGIN.get_or_init(Instant::now);
52 Self::with_sources(move || process.elapsed(), system_wall_ms)
53 }
54
55 pub fn with_sources(
64 monotonic: impl Fn() -> Duration + Send + Sync + 'static,
65 wall: impl Fn() -> i64 + Send + Sync + 'static,
66 ) -> Self {
67 let since_process = monotonic().as_nanos();
68 SystemClock {
69 monotonic: Arc::new(monotonic),
70 wall: Arc::new(wall),
71 origin_ns: grid_origin_ns(since_process),
72 }
73 }
74}
75
76fn system_wall_ms() -> i64 {
77 SystemTime::now()
78 .duration_since(UNIX_EPOCH)
79 .map(|d| d.as_millis().min(i64::MAX as u128) as i64)
80 .unwrap_or(0)
81}
82
83impl Default for SystemClock {
84 fn default() -> Self {
85 Self::new()
86 }
87}
88
89impl fmt::Debug for SystemClock {
90 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
91 f.debug_struct("SystemClock")
92 .field("origin_ns", &self.origin_ns)
93 .finish_non_exhaustive()
94 }
95}
96
97impl Clock for SystemClock {
98 fn wall_ms(&self) -> i64 {
99 (self.wall)()
100 }
101
102 fn elapsed(&self) -> Duration {
103 let now_ns = (self.monotonic)().as_nanos();
104 Duration::from_nanos(now_ns.saturating_sub(self.origin_ns).min(u64::MAX as u128) as u64)
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111 use parking_lot::Mutex;
112
113 #[test]
114 fn clocks_built_at_different_fractions_share_one_grid() {
115 let now = Arc::new(Mutex::new(Duration::from_micros(700)));
116 let source = {
117 let now = now.clone();
118 move || *now.lock()
119 };
120 let first = SystemClock::with_sources(source.clone(), || 0);
121 *now.lock() = Duration::from_micros(1_300);
122 let second = SystemClock::with_sources(source, || 0);
123 *now.lock() = Duration::from_micros(1_000_900);
124 assert_eq!(first.elapsed_ms(), 1_000);
126 assert_eq!(second.elapsed_ms(), 999);
127 *now.lock() = Duration::from_micros(1_001_000);
128 assert_eq!(second.elapsed_ms(), 1_000);
129 }
130
131 #[test]
132 fn default_clock_is_monotonic_and_shares_the_process_origin() {
133 let first = SystemClock::new();
134 let second = SystemClock::new();
135 let a = first.elapsed();
136 assert!(first.elapsed() >= a);
137 assert_eq!(first.origin_ns % 1_000_000, 0);
140 assert_eq!(second.origin_ns % 1_000_000, 0);
141 assert!(second.origin_ns >= first.origin_ns);
142 assert!(first.wall_ms() > 1_700_000_000_000);
143 }
144}