use std::cell::Cell;
use std::ptr::NonNull;
use std::alloc::{alloc, dealloc, Layout};
use std::rc::{Rc, Weak};
use std::sync::atomic::AtomicUsize;
use crate::block::{PageTaggedPtr, PageKind, Block};
use crate::common::{BLOCK_PER_PAGE, MASK_ARENA_BIT, Pointer};
pub struct PagePool<T> {
pub(crate) bitfield: usize,
pub(crate) blocks: [Block<T>; BLOCK_PER_PAGE],
pub(crate) arena_free_list: Weak<Pointer<PagePool<T>>>,
pub(crate) next_free: Pointer<PagePool<T>>,
pub(crate) next: Pointer<PagePool<T>>,
pub(crate) in_free_list: bool,
}
impl<T> PagePool<T> {
fn allocate() -> NonNull<PagePool<T>> {
let layout = Layout::new::<PagePool<T>>();
unsafe {
let page = alloc(layout) as *const PagePool<T>;
NonNull::from(&*page)
}
}
fn deallocate_page(page: *mut PagePool<T>) {
let layout = Layout::new::<PagePool<T>>();
unsafe {
std::ptr::drop_in_place(&mut (*page).arena_free_list as *mut _);
dealloc(page as *mut PagePool<T> as *mut u8, layout);
}
}
fn new(
arena_free_list: Weak<Pointer<PagePool<T>>>,
next: *mut PagePool<T>
) -> NonNull<PagePool<T>>
{
let mut page_ptr = Self::allocate();
let page_copy = page_ptr;
let page = unsafe { page_ptr.as_mut() };
page.bitfield = !0;
page.in_free_list = true;
let free_ptr = &mut page.arena_free_list as *mut Weak<Pointer<PagePool<T>>>;
unsafe {
free_ptr.write(arena_free_list);
let next_free_ptr = &mut page.next_free as *mut Pointer<_>;
let next_ptr = &mut page.next as *mut Pointer<_>;
next_free_ptr.write(Cell::new(next));
next_ptr.write(Cell::new(next));
}
for (index, block) in page.blocks.iter_mut().enumerate() {
block.page = PageTaggedPtr::new(page_copy.as_ptr() as usize, index, PageKind::Pool);
block.counter = AtomicUsize::new(0);
}
page_ptr
}
pub fn make_list(
npages: usize,
arena_free_list: &Rc<Pointer<PagePool<T>>>
) -> (NonNull<PagePool<T>>, NonNull<PagePool<T>>)
{
let arena_free_list = Rc::downgrade(arena_free_list);
let last = PagePool::<T>::new(arena_free_list.clone(), std::ptr::null_mut());
let mut previous = last;
for _ in 0..npages - 1 {
let previous_ptr = unsafe { previous.as_mut() };
let page = PagePool::<T>::new(arena_free_list.clone(), previous_ptr);
previous = page;
}
(previous, last)
}
pub(crate) fn acquire_free_block(&mut self) -> Option<NonNull<Block<T>>> {
let index_free = self.bitfield.trailing_zeros() as usize;
if index_free == BLOCK_PER_PAGE {
return None;
}
self.bitfield &= !(1 << index_free);
Some(NonNull::from(&self.blocks[index_free]))
}
pub(crate) fn drop_block(mut page: NonNull<PagePool<T>>, block: NonNull<Block<T>>) {
let page_ptr = page.as_ptr();
let page = unsafe { page.as_mut() };
let block = unsafe { block.as_ref() };
unsafe {
std::ptr::drop_in_place(block.value.get());
}
let index_in_page = block.page.index_block();
page.bitfield |= 1 << index_in_page;
if !page.bitfield == MASK_ARENA_BIT {
PagePool::<T>::deallocate_page(page_ptr);
return;
}
if !page.in_free_list {
page.in_free_list = true;
if let Some(arena_free_list) = page.arena_free_list.upgrade() {
let current = arena_free_list.get();
page.next_free.set(current);
arena_free_list.set(page_ptr);
};
}
}
}
pub(crate) fn drop_page<T>(page: *mut PagePool<T>) {
let new_bitfield = {
let page = unsafe { page.as_mut().unwrap() };
page.bitfield &= !MASK_ARENA_BIT;
page.bitfield
};
if !new_bitfield == MASK_ARENA_BIT {
PagePool::<T>::deallocate_page(page);
}
}
impl<T> Drop for PagePool<T> {
fn drop(&mut self) {
panic!("PAGE");
}
}