#[cfg(not(feature = "std"))]
use alloc::{boxed::Box, vec::Vec};
use core::cmp;
use core::mem;
use core::ptr::{self, NonNull};
use core::sync::atomic::{
AtomicPtr,
Ordering::{Acquire, Relaxed, Release},
};
use crate::hazard::Protected;
pub(crate) type Retired = reclaim::Retired<crate::HP>;
#[derive(Debug)]
pub(crate) struct RetiredBag {
pub inner: Vec<ReclaimOnDrop>,
next: Option<NonNull<RetiredBag>>,
}
impl RetiredBag {
const DEFAULT_CAPACITY: usize = 256;
#[inline]
pub fn new() -> Self {
Self { inner: Vec::with_capacity(Self::DEFAULT_CAPACITY), next: None }
}
#[inline]
pub fn merge(&mut self, mut other: Vec<ReclaimOnDrop>) {
if (other.capacity() - other.len()) > self.inner.capacity() {
mem::swap(&mut self.inner, &mut other);
}
self.inner.append(&mut other);
}
}
#[derive(Debug)]
pub(crate) struct ReclaimOnDrop(Retired);
impl ReclaimOnDrop {
#[inline]
pub fn compare_with(&self, protected: Protected) -> cmp::Ordering {
protected.address().cmp(&self.0.address())
}
}
impl From<Retired> for ReclaimOnDrop {
#[inline]
fn from(retired: Retired) -> Self {
Self(retired)
}
}
impl Drop for ReclaimOnDrop {
#[inline]
fn drop(&mut self) {
unsafe { self.0.reclaim() };
}
}
#[derive(Debug)]
pub(crate) struct AbandonedBags {
head: AtomicPtr<RetiredBag>,
}
impl AbandonedBags {
#[inline]
pub const fn new() -> Self {
Self { head: AtomicPtr::new(ptr::null_mut()) }
}
#[inline]
pub fn push(&self, abandoned: Box<RetiredBag>) {
let leaked = Box::leak(abandoned);
loop {
let head = self.head.load(Relaxed);
leaked.next = NonNull::new(head);
if self.head.compare_exchange_weak(head, leaked, Release, Relaxed).is_ok() {
return;
}
}
}
#[inline]
pub fn take_and_merge(&self) -> Option<Box<RetiredBag>> {
if self.head.load(Relaxed).is_null() {
return None;
}
let queue = unsafe { self.head.swap(ptr::null_mut(), Acquire).as_mut() };
queue.map(|bag| {
let mut boxed = unsafe { Box::from_raw(bag) };
let mut curr = boxed.next;
while let Some(ptr) = curr {
let RetiredBag { inner: bag, next } = unsafe { *Box::from_raw(ptr.as_ptr()) };
boxed.merge(bag);
curr = next;
}
boxed
})
}
}
#[cfg(test)]
mod tests {
use std::ptr::NonNull;
use std::sync::atomic::{AtomicUsize, Ordering};
use super::{AbandonedBags, ReclaimOnDrop, Retired, RetiredBag};
struct DropCount<'a>(&'a AtomicUsize);
impl Drop for DropCount<'_> {
fn drop(&mut self) {
self.0.fetch_add(1, Ordering::Relaxed);
}
}
#[test]
fn abandoned_bags() {
let count = AtomicUsize::new(0);
let mut bag1 = Box::new(RetiredBag::new());
let rec1 = NonNull::from(Box::leak(Box::new(1)));
let rec2 = NonNull::from(Box::leak(Box::new(2.2)));
let rec3 = NonNull::from(Box::leak(Box::new(String::from("String"))));
bag1.inner.push(ReclaimOnDrop::from(unsafe { Retired::new_unchecked(rec1) }));
bag1.inner.push(ReclaimOnDrop::from(unsafe { Retired::new_unchecked(rec2) }));
bag1.inner.push(ReclaimOnDrop::from(unsafe { Retired::new_unchecked(rec3) }));
let mut bag2 = Box::new(RetiredBag::new());
let rec4 = NonNull::from(Box::leak(Box::new(vec![1, 2, 3, 4])));
let rec5 = NonNull::from(Box::leak(Box::new("slice")));
bag2.inner.push(ReclaimOnDrop::from(unsafe { Retired::new_unchecked(rec4) }));
bag2.inner.push(ReclaimOnDrop::from(unsafe { Retired::new_unchecked(rec5) }));
let mut bag3 = Box::new(RetiredBag::new());
let rec6 = NonNull::from(Box::leak(Box::new(DropCount(&count))));
let rec7 = NonNull::from(Box::leak(Box::new(DropCount(&count))));
bag3.inner.push(ReclaimOnDrop::from(unsafe { Retired::new_unchecked(rec6) }));
bag3.inner.push(ReclaimOnDrop::from(unsafe { Retired::new_unchecked(rec7) }));
let abandoned = AbandonedBags::new();
abandoned.push(bag1);
abandoned.push(bag2);
abandoned.push(bag3);
let merged = abandoned.take_and_merge().unwrap();
assert_eq!(merged.inner.len(), 7);
assert_eq!(RetiredBag::DEFAULT_CAPACITY, merged.inner.capacity());
}
}