extern crate alloc;
#[cfg(feature = "thread_local_counter")]
use core::ops::Deref;
use core::sync::atomic::{AtomicPtr, Ordering};
use std::marker::PhantomData;
use alloc::sync::Arc;
#[cfg(feature = "thread_local_counter")]
use crate::epoch_counters::GlobalEpochCounterPool;
use crate::epoch_counters::{EpochCounter, EpochCounterPool};
use super::Rcu;
pub struct Arcu<T, P> {
active_value: AtomicPtr<T>,
epoch_counter_pool: P,
phantom: PhantomData<Arc<T>>,
}
#[cfg(feature = "thread_local_counter")]
impl<T: core::fmt::Display> core::fmt::Display for Arcu<T, GlobalEpochCounterPool> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
let data = self.read();
core::fmt::Display::fmt(&data.deref(), f)
}
}
impl<T: core::fmt::Debug, P> core::fmt::Debug for Arcu<T, P> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("Rcu")
.field("active_value", &"Opaque")
.field("epoch_counter_pool", &"Opaque")
.finish()
}
}
impl<T, P: EpochCounterPool> Rcu for Arcu<T, P> {
type Item = T;
type Pool = P;
#[inline]
fn new(initial: impl Into<Arc<T>>, epoch_counter_pool: P) -> Self {
Arcu {
active_value: AtomicPtr::new(Arc::into_raw(initial.into()).cast_mut()),
epoch_counter_pool,
phantom: PhantomData,
}
}
#[inline]
unsafe fn raw_read(&self, epoch_counter: &EpochCounter) -> Arc<T> {
epoch_counter.enter_rcs();
let arc_ptr = self.active_value.load(Ordering::SeqCst);
let arc = unsafe {
Arc::increment_strong_count(arc_ptr);
Arc::from_raw(arc_ptr)
};
epoch_counter.leave_rcs();
arc
}
#[inline]
fn replace(&self, new_value: impl Into<Arc<T>>) -> Arc<T> {
let arc_ptr = self.active_value.swap(
Arc::into_raw(new_value.into()).cast_mut(),
Ordering::Acquire,
);
self.epoch_counter_pool.wait_for_epochs();
unsafe { Arc::from_raw(arc_ptr) }
}
unsafe fn raw_try_update<'a>(
&self,
mut update: impl FnMut(&T) -> Option<Arc<T>>,
epoch_counter: &EpochCounter,
) -> Option<Arc<T>> {
loop {
let old = self.raw_read(epoch_counter);
let new = Arc::into_raw(update(&old)?);
let result = self.active_value.compare_exchange_weak(
Arc::as_ptr(&old).cast_mut(),
new.cast_mut(),
Ordering::AcqRel,
Ordering::Relaxed,
);
match result {
Ok(old) => {
self.epoch_counter_pool.wait_for_epochs();
return Some(unsafe { Arc::from_raw(old) });
}
Err(_new_old) => {
let _ = unsafe { Arc::from_raw(new) };
continue;
}
}
}
}
}
impl<T, P> Drop for Arcu<T, P> {
fn drop(&mut self) {
unsafe { Arc::from_raw(self.active_value.load(Ordering::Acquire)) };
}
}