use std::fmt;
use std::sync::atomic::{AtomicU64, Ordering};
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FenceToken(u64);
impl FenceToken {
pub const fn new(value: u64) -> Self {
Self(value)
}
pub const fn get(self) -> u64 {
self.0
}
}
impl fmt::Display for FenceToken {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "fence:{}", self.0)
}
}
#[derive(Debug, Default)]
pub struct Fence {
high_water: AtomicU64,
}
impl Fence {
pub const fn new() -> Self {
Self {
high_water: AtomicU64::new(0),
}
}
pub const fn with_high_water(token: u64) -> Self {
Self {
high_water: AtomicU64::new(token),
}
}
pub fn admit(&self, token: FenceToken) -> bool {
let prev = self.high_water.fetch_max(token.get(), Ordering::AcqRel);
token.get() >= prev
}
pub fn high_water(&self) -> u64 {
self.high_water.load(Ordering::Acquire)
}
}
#[cfg(test)]
mod tests {
use super::{Fence, FenceToken};
#[test]
fn admits_monotonic_and_rejects_stale() {
let fence = Fence::new();
assert!(fence.admit(FenceToken::new(5))); assert!(fence.admit(FenceToken::new(5))); assert!(fence.admit(FenceToken::new(9))); assert!(!fence.admit(FenceToken::new(7))); assert!(!fence.admit(FenceToken::new(5))); assert_eq!(fence.high_water(), 9);
}
#[test]
fn restored_fence_keeps_rejecting_below_persisted_mark() {
let fence = Fence::with_high_water(42);
assert!(!fence.admit(FenceToken::new(41)));
assert!(fence.admit(FenceToken::new(42)));
assert!(fence.admit(FenceToken::new(100)));
}
}