Skip to main content

tsoracle_core/
clock.rs

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