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(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
22 write!(f, "fence:{}", self.0)
23 }
24}
25
26#[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 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}