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