use crate::retired::{INVPTR, RetiredNode, is_rnode, rnode_unmask};
use core::sync::atomic::Ordering;
const MAX_CACHE: usize = 12;
pub unsafe trait Reclaimable: Sized {
fn retired_node(&self) -> &RetiredNode;
fn retired_node_mut(&mut self) -> &mut RetiredNode;
unsafe fn dealloc(ptr: *mut Self) {
unsafe {
drop(alloc::boxed::Box::from_raw(ptr));
}
}
}
#[inline]
pub(crate) unsafe fn get_refs_node(node: *mut RetiredNode) -> *mut RetiredNode {
let refs = unsafe { (*node).batch_link.load(Ordering::Acquire) };
if is_rnode(refs) { node } else { refs }
}
pub(crate) unsafe fn traverse(free_list: &mut *mut RetiredNode, mut next: *mut RetiredNode) {
loop {
let curr = next;
if curr.is_null() {
break;
}
if is_rnode(curr) {
let refs = rnode_unmask(curr);
let old = unsafe { (*refs).refs_or_next.fetch_sub(1, Ordering::AcqRel) };
if old == 1 {
unsafe {
(*refs).next.store(*free_list, Ordering::Relaxed);
}
*free_list = refs;
}
break;
}
next = unsafe {
(*curr)
.next
.swap(INVPTR as *mut RetiredNode, Ordering::AcqRel)
};
let refs = unsafe { (*curr).batch_link.load(Ordering::Relaxed) };
let old = unsafe { (*refs).refs_or_next.fetch_sub(1, Ordering::AcqRel) };
if old == 1 {
unsafe {
(*refs).next.store(*free_list, Ordering::Relaxed);
}
*free_list = refs;
}
}
}
pub(crate) unsafe fn traverse_cache(
free_list: &mut *mut RetiredNode,
list_count: &mut usize,
next: *mut RetiredNode,
) {
if !next.is_null() {
if *list_count >= MAX_CACHE {
unsafe { free_batch_list(*free_list) };
*free_list = core::ptr::null_mut();
*list_count = 0;
}
unsafe { traverse(free_list, next) };
*list_count += 1;
}
}
pub(crate) unsafe fn free_batch_list(mut list: *mut RetiredNode) {
while !list.is_null() {
let refs_node = list;
let batch_link = unsafe { (*refs_node).batch_link.load(Ordering::Relaxed) };
let front = rnode_unmask(batch_link);
list = unsafe { (*refs_node).next.load(Ordering::Relaxed) };
let mut curr = front;
while !curr.is_null() {
let node = unsafe { &*curr };
let next = node.batch_next();
let destructor = node.destructor();
if let Some(d) = destructor {
#[cfg(feature = "std")]
{
let _result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
unsafe { d(curr) };
}));
}
#[cfg(not(feature = "std"))]
{
unsafe { d(curr) };
}
}
curr = next;
}
}
}