daedalus_core/
clock.rs

1use std::sync::atomic::{AtomicU64, Ordering};
2
3use serde::{Deserialize, Serialize};
4
5/// Monotonic logical tick for deterministic ordering.
6///
7/// ```
8/// use daedalus_core::clock::Tick;
9/// let t = Tick::new(7);
10/// assert_eq!(t.value(), 7);
11/// ```
12#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
13#[serde(transparent)]
14pub struct Tick(u64);
15
16impl Tick {
17    pub const ZERO: Tick = Tick(0);
18
19    pub fn new(value: u64) -> Self {
20        Tick(value)
21    }
22
23    pub fn value(self) -> u64 {
24        self.0
25    }
26
27    pub fn increment(self) -> Self {
28        Tick(self.0.saturating_add(1))
29    }
30}
31
32/// Deterministic logical clock that can be manually advanced.
33///
34/// ```
35/// use daedalus_core::clock::TickClock;
36/// let clock = TickClock::default();
37/// let t1 = clock.tick();
38/// let t2 = clock.advance(4);
39/// assert!(t2.value() > t1.value());
40/// ```
41#[derive(Debug)]
42pub struct TickClock {
43    current: AtomicU64,
44}
45
46impl TickClock {
47    pub fn new(start: Tick) -> Self {
48        Self {
49            current: AtomicU64::new(start.value()),
50        }
51    }
52
53    pub fn default_start() -> Self {
54        Self::new(Tick::ZERO)
55    }
56
57    /// Returns the current tick without advancing.
58    pub fn now_tick(&self) -> Tick {
59        Tick(self.current.load(Ordering::Relaxed))
60    }
61
62    /// Advances the clock by `delta` and returns the new tick.
63    pub fn advance(&self, delta: u64) -> Tick {
64        let next = self.current.fetch_add(delta, Ordering::Relaxed) + delta;
65        Tick(next)
66    }
67
68    /// Advance by one tick.
69    pub fn tick(&self) -> Tick {
70        self.advance(1)
71    }
72}
73
74impl Default for TickClock {
75    fn default() -> Self {
76        Self::default_start()
77    }
78}
79
80#[cfg(test)]
81mod tests {
82    use super::*;
83
84    #[test]
85    fn monotonic_ticks() {
86        let clock = TickClock::default();
87        assert_eq!(clock.now_tick(), Tick::ZERO);
88        let t1 = clock.tick();
89        let t2 = clock.tick();
90        assert!(t2.value() > t1.value());
91    }
92}