use crate::config::MAX_ACTIVE_EPOCHS;
use std::sync::Mutex;
use std::sync::atomic::{AtomicUsize, Ordering};
pub struct EpochBasedReclamation {
global_epoch: AtomicUsize,
reader_counts: [AtomicUsize; MAX_ACTIVE_EPOCHS],
pending_disposals: Mutex<Vec<Box<dyn FnOnce() + Send>>>,
}
impl EpochBasedReclamation {
#[must_use]
pub fn new() -> Self {
Self {
global_epoch: AtomicUsize::new(0),
reader_counts: std::array::from_fn(|_| AtomicUsize::new(0)),
pending_disposals: Mutex::new(Vec::new()),
}
}
pub fn enter_epoch(&self) -> EpochGuard<'_> {
let current = self.global_epoch.load(Ordering::Relaxed);
let idx = current % MAX_ACTIVE_EPOCHS;
self.reader_counts[idx].fetch_add(1, Ordering::Relaxed);
EpochGuard { system: self, index: idx }
}
pub fn advance_epoch(&self) {
self.global_epoch.fetch_add(1, Ordering::Release);
let current = self.global_epoch.load(Ordering::Acquire);
if current >= 2 {
let safe_idx = (current - 2) % MAX_ACTIVE_EPOCHS;
if self.reader_counts[safe_idx].load(Ordering::Acquire) == 0 {
let mut guard = self.pending_disposals.lock().unwrap();
for cb in guard.drain(..) {
cb();
}
}
}
}
pub fn queue_disposal(&self, callback: impl FnOnce() + Send + 'static) {
self.pending_disposals.lock().unwrap().push(Box::new(callback));
}
}
impl Default for EpochBasedReclamation {
fn default() -> Self {
Self::new()
}
}
pub struct EpochGuard<'a> {
system: &'a EpochBasedReclamation,
index: usize,
}
impl Drop for EpochGuard<'_> {
fn drop(&mut self) {
self.system.reader_counts[self.index].fetch_sub(1, Ordering::Release);
}
}