use core::{
cell::Cell,
fmt::{self, Debug},
sync::atomic::{AtomicU64, Ordering},
};
pub struct EpochCounter {
value: AtomicU64,
}
impl Default for EpochCounter {
fn default() -> Self {
Self::new()
}
}
impl EpochCounter {
pub const fn new() -> Self {
EpochCounter {
value: AtomicU64::new(0),
}
}
#[inline]
pub fn current(&self) -> EpochId {
EpochId {
value: self.value.load(Ordering::Relaxed),
}
}
#[inline]
pub fn current_mut(&mut self) -> EpochId {
EpochId {
value: *self.value.get_mut(),
}
}
#[inline]
pub fn next(&self) -> EpochId {
let old = self.value.fetch_add(1, Ordering::Relaxed);
debug_assert!(old < u64::MAX);
EpochId { value: old + 1 }
}
#[inline]
pub fn next_mut(&mut self) -> EpochId {
let value = self.value.get_mut();
debug_assert!(*value < u64::MAX);
*value += 1;
EpochId { value: *value }
}
#[inline]
pub fn next_if(&self, cond: bool) -> EpochId {
if cond {
self.next()
} else {
self.current()
}
}
}
#[derive(Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct EpochId {
value: u64,
}
impl Default for EpochId {
fn default() -> Self {
EpochId::start()
}
}
impl Debug for EpochId {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> core::fmt::Result {
<u64 as Debug>::fmt(&self.value, f)
}
}
impl EpochId {
#[inline]
pub const fn start() -> Self {
EpochId { value: 0 }
}
#[inline]
pub const fn before(&self, other: EpochId) -> bool {
self.value < other.value
}
#[inline]
pub const fn after(&self, other: EpochId) -> bool {
self.value > other.value
}
#[inline]
pub fn update(&mut self, other: EpochId) {
self.value = self.value.max(other.value);
}
#[inline]
pub fn bump(&mut self, to: EpochId) {
debug_assert!(
self.before(to),
"`EpochId::bump` must be used only for older epochs"
);
*self = to;
}
#[inline]
pub fn bump_again(&mut self, to: EpochId) {
debug_assert!(
!self.after(to),
"`EpochId::bump` must be used only for older epochs"
);
*self = to;
}
#[inline]
pub fn bump_cell(cell: &Cell<Self>, to: EpochId) {
debug_assert!(
!cell.get().after(to),
"`EpochId::bump_cell` must be used only for older or same epochs"
);
cell.set(to);
}
}