1use std::fmt;
4use std::sync::atomic::{AtomicU64, Ordering};
5
6#[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#[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 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}