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;
pub(in crate::arena) struct RetiredLocalChunks<A: Allocator + Clone> {
head: Cell<*mut u8>,
_marker: PhantomData<ChunkMutator<A>>,
}
unsafe impl<A: Allocator + Clone + Send> Send for RetiredLocalChunks<A> {}
impl<A: Allocator + Clone> RetiredLocalChunks<A> {
#[inline]
pub(in crate::arena) const fn new() -> Self {
Self {
head: Cell::new(ptr::null_mut()),
_marker: PhantomData,
}
}
#[inline]
pub(in crate::arena) fn push(&self, mutator: ChunkMutator<A>) {
let Some(chunk) = mutator.forget_into_chunk() else {
return;
};
unsafe {
let prev_head = self.head.replace(chunk.cast::<u8>().as_ptr());
Chunk::set_next(chunk, prev_head);
}
}
pub(in crate::arena) 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 = Chunk::<A>::header_to_fat(cur);
let chunk = NonNull::new_unchecked(fat);
let next = Chunk::next(chunk);
Chunk::set_next(chunk, ptr::null_mut());
Self::release_retired_chunk(chunk);
cur = next;
}
}
}
}
#[inline]
unsafe fn release_retired_chunk(chunk: NonNull<Chunk<A>>) {
unsafe {
if chunk.as_ref().dec_ref() {
Chunk::teardown_and_release(chunk);
}
}
}
}
impl<A: Allocator + Clone> Drop for RetiredLocalChunks<A> {
fn drop(&mut self) {
self.clear();
}
}
#[cfg(test)]
mod tests {
use allocator_api2::alloc::Global;
use super::*;
#[test]
fn push_chunkless_mutator_is_noop() {
let retired = RetiredLocalChunks::<Global>::new();
retired.push(ChunkMutator::<Global>::empty());
}
}