Skip to main content

orbit_lock/
fence.rs

1//! Resource-side fencing for lock tenures.
2
3use std::fmt;
4use std::sync::atomic::{AtomicU64, Ordering};
5
6/// Monotonic identity of one lock tenure.
7#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
8pub struct FenceToken(u64);
9
10impl FenceToken {
11    pub const fn new(value: u64) -> Self {
12        Self(value)
13    }
14
15    pub const fn get(self) -> u64 {
16        self.0
17    }
18}
19
20impl fmt::Display for FenceToken {
21    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22        write!(f, "fence:{}", self.0)
23    }
24}
25
26/// Atomic resource-side high-water mark for fencing stale holders.
27#[derive(Debug, Default)]
28pub struct Fence {
29    high_water: AtomicU64,
30}
31
32impl Fence {
33    pub const fn new() -> Self {
34        Self {
35            high_water: AtomicU64::new(0),
36        }
37    }
38
39    pub const fn with_high_water(token: u64) -> Self {
40        Self {
41            high_water: AtomicU64::new(token),
42        }
43    }
44
45    /// Admit this tenure unless a newer tenure was already observed.
46    pub fn admit(&self, token: FenceToken) -> bool {
47        let previous = self.high_water.fetch_max(token.get(), Ordering::AcqRel);
48        token.get() >= previous
49    }
50
51    pub fn high_water(&self) -> u64 {
52        self.high_water.load(Ordering::Acquire)
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use super::{Fence, FenceToken};
59
60    #[test]
61    fn admits_current_and_newer_tenures_but_rejects_stale_ones() {
62        let fence = Fence::new();
63        assert!(fence.admit(FenceToken::new(5)));
64        assert!(fence.admit(FenceToken::new(5)));
65        assert!(fence.admit(FenceToken::new(9)));
66        assert!(!fence.admit(FenceToken::new(7)));
67        assert_eq!(fence.high_water(), 9);
68    }
69
70    #[test]
71    fn restored_high_water_keeps_rejecting_older_tenures() {
72        let fence = Fence::with_high_water(42);
73        assert!(!fence.admit(FenceToken::new(41)));
74        assert!(fence.admit(FenceToken::new(42)));
75        assert!(fence.admit(FenceToken::new(100)));
76    }
77}