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(
22        &self,
23        f: &mut fmt::Formatter<'_>
24    ) -> fmt::Result {
25        write!(f, "fence:{}", self.0)
26    }
27}
28
29/// Atomic resource-side high-water mark for fencing stale holders.
30#[derive(Debug, Default)]
31pub struct Fence {
32    high_water: AtomicU64
33}
34
35impl Fence {
36    pub const fn new() -> Self {
37        Self { high_water: AtomicU64::new(0) }
38    }
39
40    pub const fn with_high_water(token: u64) -> Self {
41        Self { high_water: AtomicU64::new(token) }
42    }
43
44    /// Admit this tenure unless a newer tenure was already observed.
45    pub fn admit(
46        &self,
47        token: FenceToken
48    ) -> bool {
49        let previous = self.high_water.fetch_max(token.get(), Ordering::AcqRel);
50        token.get() >= previous
51    }
52
53    pub fn high_water(&self) -> u64 {
54        self.high_water.load(Ordering::Acquire)
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::{Fence, FenceToken};
61
62    #[test]
63    fn admits_current_and_newer_tenures_but_rejects_stale_ones() {
64        let fence = Fence::new();
65        assert!(fence.admit(FenceToken::new(5)));
66        assert!(fence.admit(FenceToken::new(5)));
67        assert!(fence.admit(FenceToken::new(9)));
68        assert!(!fence.admit(FenceToken::new(7)));
69        assert_eq!(fence.high_water(), 9);
70    }
71
72    #[test]
73    fn restored_high_water_keeps_rejecting_older_tenures() {
74        let fence = Fence::with_high_water(42);
75        assert!(!fence.admit(FenceToken::new(41)));
76        assert!(fence.admit(FenceToken::new(42)));
77        assert!(fence.admit(FenceToken::new(100)));
78    }
79}