git_bug/replica/entity/lamport/
mem.rs

1// git-bug-rs - A rust library for interfacing with git-bug repositories
2//
3// Copyright (C) 2025 Benedikt Peetz <benedikt.peetz@b-peetz.de>
4// SPDX-License-Identifier: GPL-3.0-or-later
5//
6// This file is part of git-bug-rs/git-gub.
7//
8// You should have received a copy of the License along with this program.
9// If not, see <https://www.gnu.org/licenses/agpl.txt>.
10
11//! An in-memory implementation of a [`Clock`][`super::Clock`].
12
13use std::sync::atomic::{AtomicU64, Ordering};
14
15use super::Clock;
16
17/// A thread safe implementation of a lamport clock.
18/// It uses efficient atomic operations for all of its functions.
19#[derive(Debug)]
20pub struct MemClock {
21    counter: AtomicU64,
22}
23
24impl Clock for MemClock {
25    type IncrementError = std::convert::Infallible;
26    type WitnessError = std::convert::Infallible;
27
28    fn time(&self) -> super::Time {
29        super::Time(self.counter.load(Ordering::SeqCst))
30    }
31
32    fn increment(&mut self) -> Result<super::Time, Self::IncrementError> {
33        Ok(super::Time(self.counter.fetch_add(1, Ordering::SeqCst)))
34    }
35
36    fn witness(&mut self, time: super::Time) -> Result<(), Self::WitnessError> {
37        let current = self.time().0;
38        let other = time.0;
39
40        if other <= current {
41            return Ok(());
42        }
43
44        // Ensure that our local clock is at least one ahead.
45        while self
46            .counter
47            .compare_exchange(current, other, Ordering::SeqCst, Ordering::SeqCst)
48            != Ok(current)
49        {
50            // The compare_exchange failed, so we just retry. Eventually it
51            // should succeed or a future witness will pass us by
52            // and our witness will end.
53            // TODO(@bpeetz): This should be bounded, shouldn't it? <2025-04-15>
54        }
55
56        Ok(())
57    }
58}
59
60impl Default for MemClock {
61    fn default() -> Self {
62        Self::new()
63    }
64}
65
66impl MemClock {
67    /// Create a new [`MemClock`].
68    /// The starting value is 1.
69    #[must_use]
70    pub fn new() -> Self {
71        Self {
72            counter: AtomicU64::new(1),
73        }
74    }
75
76    /// Creates a new in memory clock, with the initial time value of `value`.
77    ///
78    /// # Panics
79    /// If the value is invalid (i.e., the value is equal to 0).
80    #[must_use]
81    pub fn new_with_value(value: u64) -> Self {
82        if value == 0 {
83            todo!("Read invalid value from persisted clock.")
84        }
85
86        Self {
87            counter: AtomicU64::new(value),
88        }
89    }
90}
91
92#[cfg(test)]
93mod test {
94    use super::MemClock;
95    use crate::replica::entity::lamport::test::test_clock;
96
97    #[test]
98    fn test_mem_clock() {
99        let clock = MemClock::new();
100        test_clock(clock);
101    }
102}