Skip to main content

synckit_core/awareness/
clock.rs

1/// Increasing Clock for Awareness Conflict Resolution
2///
3/// Unlike vector clocks (used for CRDTs), awareness uses a simple
4/// monotonically increasing clock per client. This is sufficient because:
5/// - Awareness state is ephemeral (no complex merge semantics)
6/// - Last-write-wins at field level is acceptable
7/// - Simpler = faster for high-frequency updates (cursor positions)
8use std::sync::atomic::{AtomicU64, Ordering};
9
10/// Thread-safe increasing clock
11#[derive(Debug)]
12pub struct IncreasingClock {
13    value: AtomicU64,
14}
15
16impl IncreasingClock {
17    /// Create a new clock starting at 0
18    pub fn new() -> Self {
19        Self {
20            value: AtomicU64::new(0),
21        }
22    }
23
24    /// Increment and return the new value
25    pub fn increment(&self) -> u64 {
26        self.value.fetch_add(1, Ordering::SeqCst) + 1
27    }
28
29    /// Get current value without incrementing
30    pub fn get(&self) -> u64 {
31        self.value.load(Ordering::SeqCst)
32    }
33
34    /// Set to a specific value (used when receiving remote updates)
35    pub fn set(&self, value: u64) {
36        self.value.store(value, Ordering::SeqCst);
37    }
38
39    /// Update to max of current and provided value
40    /// (ensures monotonicity when receiving updates)
41    pub fn update_to_max(&self, other: u64) {
42        self.value.fetch_max(other, Ordering::SeqCst);
43    }
44}
45
46impl Default for IncreasingClock {
47    fn default() -> Self {
48        Self::new()
49    }
50}
51
52impl Clone for IncreasingClock {
53    fn clone(&self) -> Self {
54        Self {
55            value: AtomicU64::new(self.get()),
56        }
57    }
58}
59
60#[cfg(test)]
61mod tests {
62    use super::*;
63
64    #[test]
65    fn test_clock_increment() {
66        let clock = IncreasingClock::new();
67        assert_eq!(clock.get(), 0);
68        assert_eq!(clock.increment(), 1);
69        assert_eq!(clock.increment(), 2);
70        assert_eq!(clock.get(), 2);
71    }
72
73    #[test]
74    fn test_clock_update_to_max() {
75        let clock = IncreasingClock::new();
76        clock.set(5);
77        clock.update_to_max(3); // Should not decrease
78        assert_eq!(clock.get(), 5);
79        clock.update_to_max(10); // Should increase
80        assert_eq!(clock.get(), 10);
81    }
82}