Skip to main content

tsoracle_core/
clock.rs

1//! Source of physical-time milliseconds for the TSO algorithm.
2//!
3//! The Allocator's monotonicity is independent of clock correctness — a clock
4//! jumping backward cannot cause timestamp regression because the persisted
5//! high-water always wins. A clock pinned far in the past stalls new windows
6//! until wall time catches up past the persisted high-water.
7
8#[cfg(any(test, feature = "test-clock"))]
9use core::sync::atomic::{AtomicU64, Ordering};
10
11pub trait Clock: Send + Sync + 'static {
12    /// Milliseconds since Unix epoch.
13    fn now_ms(&self) -> u64;
14}
15
16/// Default implementation backed by `std::time::SystemTime`.
17pub struct SystemClock;
18
19impl Clock for SystemClock {
20    fn now_ms(&self) -> u64 {
21        std::time::SystemTime::now()
22            .duration_since(std::time::UNIX_EPOCH)
23            .map(|d| d.as_millis() as u64)
24            .unwrap_or(0)
25    }
26}
27
28#[cfg(any(test, feature = "test-clock"))]
29pub mod testing {
30    use super::*;
31    use std::sync::Arc;
32
33    /// Hand-driven clock for deterministic tests.
34    #[derive(Clone, Default)]
35    pub struct MockClock {
36        ms: Arc<AtomicU64>,
37    }
38
39    impl MockClock {
40        pub fn new(start_ms: u64) -> Self {
41            MockClock {
42                ms: Arc::new(AtomicU64::new(start_ms)),
43            }
44        }
45        pub fn advance(&self, by_ms: u64) {
46            self.ms.fetch_add(by_ms, Ordering::AcqRel);
47        }
48        pub fn set(&self, to_ms: u64) {
49            self.ms.store(to_ms, Ordering::Release);
50        }
51    }
52
53    impl Clock for MockClock {
54        fn now_ms(&self) -> u64 {
55            self.ms.load(Ordering::Acquire)
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63    use testing::MockClock;
64
65    #[test]
66    fn system_clock_returns_nonzero() {
67        let clock = SystemClock;
68        let now = clock.now_ms();
69        assert!(now > 1_700_000_000_000, "current time after 2023-11");
70    }
71
72    #[test]
73    fn mock_clock_starts_at_seed() {
74        let clock = MockClock::new(42);
75        assert_eq!(clock.now_ms(), 42);
76    }
77
78    #[test]
79    fn mock_clock_advance() {
80        let clock = MockClock::new(100);
81        clock.advance(50);
82        assert_eq!(clock.now_ms(), 150);
83    }
84
85    #[test]
86    fn mock_clock_set() {
87        let clock = MockClock::new(100);
88        clock.set(999);
89        assert_eq!(clock.now_ms(), 999);
90    }
91}