#[cfg(feature = "bicephany")]
mod bicephaly;
#[cfg(not(feature = "bicephany"))]
pub(crate) mod hazard_pointer_list;
mod list;
mod reclaim_strategy;
use crate::macros::conditional_const;
use crate::sync::{AtomicPtr, Ordering};
use alloc::boxed::Box;
#[cfg(not(feature = "std"))]
use alloc::collections::BTreeSet as Set;
#[cfg(feature = "bicephany")]
use bicephaly::Bicephaly;
use list::{LockFreeList, Node};
pub use reclaim_strategy::{ReclaimStrategy, TimedCappedSettings};
#[cfg(feature = "std")]
use std::collections::HashSet as Set;
#[cfg(not(feature = "bicephany"))]
use self::hazard_pointer_list::HazardPointerList;
pub(crate) trait Retirable {}
#[cfg(not(feature = "bicephany"))]
pub(crate) type HazardPointer<'a> = Pointer<'a, hazard_pointer_list::Node>;
#[cfg(not(feature = "bicephany"))]
type HazardPointers = HazardPointerList;
#[cfg(feature = "bicephany")]
pub(crate) type HazardPointer<'a> = Pointer<'a, bicephaly::Node<AtomicPtr<usize>>>;
#[cfg(feature = "bicephany")]
type HazardPointers = Bicephaly<AtomicPtr<usize>>;
#[cfg(not(test))]
pub(crate) struct Pointer<'a, T>(&'a T);
#[cfg(test)]
pub(crate) struct Pointer<'a, T>(pub(super) &'a T);
impl<'a, T> Pointer<'a, T> {
fn new(value: &'a T) -> Self {
Pointer(value)
}
}
impl<'a> HazardPointer<'a> {
pub(crate) fn reset(&self) {
self.0.store(core::ptr::null_mut(), Ordering::Release);
}
pub(crate) fn protect(&self, ptr: *mut usize) {
self.0.store(ptr, Ordering::Release);
}
}
impl<T> Retirable for T {}
#[derive(Debug)]
struct Retire {
ptr: *mut usize,
retirable: *mut dyn Retirable,
}
impl Retire {
fn new<T>(ptr: *mut T) -> Self {
Self {
ptr: ptr as *mut usize,
retirable: ptr as *mut dyn Retirable,
}
}
}
#[derive(Debug)]
pub struct Domain<const DOMAIN_ID: usize> {
retired: LockFreeList<Retire>,
hazard_ptrs: HazardPointers,
reclaim_strategy: ReclaimStrategy,
}
impl<const DOMAIN_ID: usize> Domain<DOMAIN_ID> {
#[cfg(not(loom))]
pub(crate) const fn default() -> Self {
Self::_new(ReclaimStrategy::default())
}
conditional_const!(
pub fn new(reclaim_strategy: ReclaimStrategy) -> Self {
#[cfg(all(nightly, not(loom)))]
assert!(DOMAIN_ID != crate::SHARED_DOMAIN_ID);
Self::_new(reclaim_strategy)
}
);
conditional_const!(
pub(crate) fn _new(reclaim_strategy: ReclaimStrategy) -> Self {
Self {
hazard_ptrs: HazardPointers::new(),
retired: LockFreeList::new(),
reclaim_strategy,
}
}
);
pub(crate) fn acquire_haz_ptr(&self) -> HazardPointer<'_> {
if let Some(haz_ptr) = self.hazard_ptrs.get_available() {
HazardPointer::new(haz_ptr)
} else {
self.acquire_new_haz_ptr()
}
}
pub(crate) fn release_hazard_ptr(&self, haz_ptr: HazardPointer) {
haz_ptr.reset();
self.hazard_ptrs.set_node_available(haz_ptr.0);
}
fn acquire_new_haz_ptr(&self) -> HazardPointer<'_> {
HazardPointer::new(
self.hazard_ptrs
.push_in_use(AtomicPtr::new(core::ptr::null_mut())),
)
}
pub(crate) unsafe fn retire<T>(&self, value: *mut T) {
core::sync::atomic::fence(Ordering::SeqCst);
self.retired.push(Retire::new(value));
if self.should_reclaim() {
self.bulk_reclaim();
}
}
fn should_reclaim(&self) -> bool {
self.reclaim_strategy.should_reclaim(
self.retired.count.load(Ordering::Acquire),
self.retired.count.load(Ordering::Acquire),
)
}
pub fn reclaim(&self) -> usize {
self.bulk_reclaim()
}
fn bulk_reclaim(&self) -> usize {
let retired_list = self
.retired
.head
.swap(core::ptr::null_mut(), Ordering::Acquire);
core::sync::atomic::fence(Ordering::SeqCst);
self.retired.count.store(0, Ordering::Release);
if retired_list.is_null() {
return 0;
}
let guarded_ptrs = self.get_guarded_ptrs();
self.reclaim_unguarded(guarded_ptrs, retired_list)
}
fn reclaim_unguarded(
&self,
guarded_ptrs: Set<*const usize>,
retired_list: *mut Node<Retire>,
) -> usize {
let mut node_ptr = retired_list;
let mut still_retired = core::ptr::null_mut();
let mut tail_ptr = None;
let mut reclaimed = 0;
let mut number_remaining = 0;
while !node_ptr.is_null() {
let node = unsafe { &*node_ptr };
let next = node.next.load(Ordering::Relaxed);
if guarded_ptrs.contains(&(node.value.ptr as *const usize)) {
node.next.store(still_retired, Ordering::Relaxed);
still_retired = node_ptr;
if tail_ptr.is_none() {
tail_ptr = Some(&node.next);
}
number_remaining += 1;
} else {
unsafe { core::ptr::drop_in_place(node.value.retirable) };
let _node = unsafe { Box::from_raw(node_ptr) };
reclaimed += 1;
}
node_ptr = next;
}
if let Some(tail) = tail_ptr {
core::sync::atomic::fence(Ordering::SeqCst);
unsafe { self.retired.push_all(still_retired, tail, number_remaining) };
}
reclaimed
}
fn get_guarded_ptrs(&self) -> Set<*const usize> {
self.hazard_ptrs
.iter()
.filter_map(|haz_ptr| {
let guarded_ptr = haz_ptr.load(Ordering::Acquire);
if guarded_ptr.is_null() {
None
} else {
Some(guarded_ptr as *const usize)
}
})
.collect()
}
}
impl<const DOMAIN_ID: usize> Drop for Domain<DOMAIN_ID> {
fn drop(&mut self) {
self.bulk_reclaim();
assert!(self.retired.head.load(Ordering::Relaxed).is_null());
}
}