1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120
use core::sync::atomic::{AtomicU64, Ordering}; const PIN_MASK: u64 = core::u64::MAX >> 1; #[derive(Default, Debug, Clone, Copy)] pub struct Epoch { data: u64, } impl Epoch { pub const AMOUNT: u64 = 3; pub const ZERO: Self = Self::from_raw(0); const fn from_raw(data: u64) -> Self { Self { data } } pub fn into_raw(self) -> u64 { self.data } pub fn is_pinned(self) -> bool { (self.data & !PIN_MASK) != 0 } pub fn pinned(self) -> Self { Self::from_raw(self.data | !PIN_MASK) } pub fn unpinned(self) -> Self { Self::from_raw(self.data & PIN_MASK) } pub fn next(self) -> Self { debug_assert!(!self.is_pinned()); Self::from_raw(self.data + 1) } pub fn two_passed(self, now: Epoch) -> bool { now.data.saturating_sub(self.data) >= 2 } fn unique(self) -> u64 { self.data % Self::AMOUNT } } impl PartialEq for Epoch { fn eq(&self, other: &Self) -> bool { self.unique() == other.unique() } } #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct DefinitiveEpoch(pub u64); impl From<Epoch> for DefinitiveEpoch { fn from(epoch: Epoch) -> Self { Self(epoch.data) } } pub struct AtomicEpoch { raw: AtomicU64, } impl AtomicEpoch { pub fn new(epoch: Epoch) -> Self { Self { raw: AtomicU64::new(epoch.into_raw()), } } pub fn load(&self, order: Ordering) -> Epoch { let raw = self.raw.load(order); Epoch::from_raw(raw) } pub fn store(&self, epoch: Epoch, order: Ordering) { let raw = epoch.into_raw(); self.raw.store(raw, order); } pub fn compare_and_set_non_unique(&self, current: Epoch, new: Epoch, order: Ordering) { let current_raw = current.into_raw(); let new_raw = new.into_raw(); self.raw.compare_and_swap(current_raw, new_raw, order); } pub fn try_advance(&self, current: Epoch) -> Result<Epoch, ()> { let current_raw = current.into_raw(); let next = current.next(); let next_raw = next.into_raw(); let did_advance = self.raw .compare_exchange(current_raw, next_raw, Ordering::SeqCst, Ordering::Relaxed); if did_advance.is_ok() { Ok(next) } else { Err(()) } } } #[cfg(test)] mod tests { use super::Epoch; #[test] fn pin_check() { let epoch = Epoch::ZERO; assert!(!epoch.is_pinned()); let pinned = epoch.pinned(); assert!(pinned.is_pinned()); let unpinned_next = epoch.unpinned().next(); assert!(!unpinned_next.is_pinned()); } }