use core::cell::Cell;
use core::marker::PhantomData;
use core::ptr::{self, NonNull};
use allocator_api2::alloc::Allocator;
use crate::internal::chunk::Chunk;
use crate::internal::chunk_mutator::ChunkMutator;
use crate::internal::local_chunk::LocalChunk;
pub(crate) struct RetiredLocalChunks<A: Allocator + Clone> {
head: Cell<*mut u8>,
_marker: PhantomData<ChunkMutator<LocalChunk<A>>>,
}
unsafe impl<A: Allocator + Clone + Send> Send for RetiredLocalChunks<A> {}
impl<A: Allocator + Clone> RetiredLocalChunks<A> {
#[inline]
pub(crate) const fn new() -> Self {
Self {
head: Cell::new(ptr::null_mut()),
_marker: PhantomData,
}
}
#[inline]
pub(crate) fn push(&self, mutator: ChunkMutator<LocalChunk<A>>) {
let Some(chunk) = mutator.forget_into_chunk() else {
return;
};
unsafe {
let prev_head = self.head.replace(chunk.cast::<u8>().as_ptr());
LocalChunk::set_next(chunk, prev_head);
}
}
pub(crate) fn clear(&self) {
loop {
let mut cur = self.head.replace(ptr::null_mut());
if cur.is_null() {
return;
}
while !cur.is_null() {
unsafe {
let fat = LocalChunk::<A>::header_to_fat(cur);
let chunk = NonNull::new_unchecked(fat);
let next = LocalChunk::set_next(chunk, ptr::null_mut());
Self::release_retired_chunk(chunk);
cur = next;
}
}
}
}
#[inline]
unsafe fn release_retired_chunk(chunk: NonNull<LocalChunk<A>>) {
use crate::internal::chunk_ops::ChunkOps;
unsafe {
let chunk_ref = chunk.as_ref();
let last = chunk_ref.dec_ref();
debug_assert!(last, "retired LocalChunk refcount must be 1; dec_ref must hit zero");
if last {
<LocalChunk<A> as ChunkOps>::teardown_and_release(chunk);
}
}
}
}
impl<A: Allocator + Clone> Drop for RetiredLocalChunks<A> {
fn drop(&mut self) {
self.clear();
}
}