use core::sync::atomic::Ordering::SeqCst;
use core::sync::atomic::AtomicPtr;
use core::mem::{drop, forget};
use core::ptr::null_mut;
use alloc::boxed::Box;
use super::block::Block;
use crate::try_swap_ptr;
#[repr(transparent)]
pub struct BlockPointer<const L: usize, const F: usize, T> {
inner: AtomicPtr<Block<L, F, T>>,
}
impl<const L: usize, const F: usize, T> BlockPointer<L, F, T> {
pub fn new() -> Self {
Self {
inner: AtomicPtr::new(null_mut()),
}
}
pub fn load(&self) -> Option<&Block<L, F, T>> {
let block_ptr = self.inner.load(SeqCst);
unsafe { block_ptr.as_ref() }
}
pub fn try_collect(&self, revision: usize) -> Option<CollectedBlock<L, F, T>> {
let this = self.load()?;
let next = this.next.load()?;
if !this.fully_consumed() {
return None;
}
let offset = this.offset.load(SeqCst);
assert_eq!(next.offset.load(SeqCst), 0);
next.offset.store(offset + L, SeqCst);
let this_ptr = this as *const _ as *mut _;
let next_ptr = next as *const _ as *mut _;
if try_swap_ptr(&self.inner, this_ptr, next_ptr) {
let collected = CollectedBlock {
revision,
block_ptr: this_ptr,
};
Some(collected)
} else {
None
}
}
fn append(&self, tail: *mut Block<L, F, T>) {
let mut this = self;
loop {
match try_swap_ptr(&this.inner, null_mut(), tail) {
true => break,
false => this = &this.load().unwrap().next,
}
}
}
pub fn append_new(&self) {
self.append(Box::leak(Box::default()));
}
pub fn recycle(&self, collected: CollectedBlock<L, F, T>) {
self.append(collected.recycle());
}
fn forget(&self) {
self.inner.store(null_mut(), SeqCst);
}
}
pub struct CollectedBlock<const L: usize, const F: usize, T> {
pub revision: usize,
block_ptr: *mut Block<L, F, T>,
}
impl<const L: usize, const F: usize, T> CollectedBlock<L, F, T> {
fn recycle(self) -> *mut Block<L, F, T> {
let block_ptr = self.block_ptr;
forget(self);
let block = unsafe { &*block_ptr };
block.next.forget();
block.offset.store(0, SeqCst);
block.reset_flags();
block_ptr
}
}
impl<const L: usize, const F: usize, T> Drop for BlockPointer<L, F, T> {
fn drop(&mut self) {
let mut block_ptr = self.inner.swap(null_mut(), SeqCst);
while !block_ptr.is_null() {
let block = unsafe { Box::from_raw(block_ptr) };
block_ptr = block.next.inner.swap(null_mut(), SeqCst);
drop(block);
}
}
}
impl<const L: usize, const F: usize, T> Drop for CollectedBlock<L, F, T> {
fn drop(&mut self) {
let block = unsafe { Box::from_raw(self.block_ptr) };
block.next.forget();
drop(block);
}
}