use alloc::boxed::Box;
use core::{f64, marker::PhantomData, usize};
use crate::{
context::{Context, Mutation},
Collect,
};
#[derive(Debug, Clone)]
pub struct ArenaParameters {
pub(crate) pause_factor: f64,
pub(crate) timing_factor: f64,
pub(crate) min_sleep: usize,
}
impl Default for ArenaParameters {
fn default() -> ArenaParameters {
const PAUSE_FACTOR: f64 = 0.5;
const TIMING_FACTOR: f64 = 1.5;
const MIN_SLEEP: usize = 4096;
ArenaParameters {
pause_factor: PAUSE_FACTOR,
timing_factor: TIMING_FACTOR,
min_sleep: MIN_SLEEP,
}
}
}
impl ArenaParameters {
pub fn set_pause_factor(mut self, pause_factor: f64) -> ArenaParameters {
assert!(pause_factor >= 0.0);
self.pause_factor = pause_factor;
self
}
pub fn set_timing_factor(mut self, timing_factor: f64) -> ArenaParameters {
assert!(timing_factor >= 0.0);
self.timing_factor = timing_factor;
self
}
pub fn set_min_sleep(mut self, min_sleep: usize) -> ArenaParameters {
self.min_sleep = min_sleep;
self
}
}
pub trait Rootable<'a>: 'static {
type Root: Collect + 'a;
}
#[doc(hidden)]
pub struct __DynRootable<T: ?Sized>(PhantomData<T>);
impl<'a, T: ?Sized + Rootable<'a>> Rootable<'a> for __DynRootable<T> {
type Root = <T as Rootable<'a>>::Root;
}
#[macro_export]
macro_rules! Rootable {
($gc:lifetime => $root:ty) => {
$crate::__DynRootable::<dyn for<$gc> $crate::Rootable<$gc, Root = $root>>
};
($root:ty) => {
$crate::Rootable!['__gc => $crate::__unelide_lifetimes!('__gc; $root)]
};
}
pub type Root<'a, R> = <R as Rootable<'a>>::Root;
pub struct Arena<R: for<'a> Rootable<'a>> {
root: Root<'static, R>,
context: Box<Context>,
}
impl<R: for<'a> Rootable<'a>> Arena<R> {
pub fn new<F>(arena_parameters: ArenaParameters, f: F) -> Arena<R>
where
F: for<'gc> FnOnce(&'gc Mutation<'gc>) -> Root<'gc, R>,
{
unsafe {
let context = Box::new(Context::new(arena_parameters));
let mc: &'static Mutation<'_> = &*(context.mutation_context() as *const _);
let root: Root<'static, R> = f(mc);
Arena { context, root }
}
}
pub fn try_new<F, E>(arena_parameters: ArenaParameters, f: F) -> Result<Arena<R>, E>
where
F: for<'gc> FnOnce(&'gc Mutation<'gc>) -> Result<Root<'gc, R>, E>,
{
unsafe {
let context = Box::new(Context::new(arena_parameters));
let mc: &'static Mutation<'_> = &*(context.mutation_context() as *const _);
let root: Root<'static, R> = f(mc)?;
Ok(Arena { context, root })
}
}
#[inline]
pub fn mutate<F, T>(&self, f: F) -> T
where
F: for<'gc> FnOnce(&'gc Mutation<'gc>, &'gc Root<'gc, R>) -> T,
{
unsafe {
let mc: &'static Mutation<'_> = &*(self.context.mutation_context() as *const _);
let root: &'static Root<'_, R> = &*(&self.root as *const _);
f(mc, root)
}
}
#[inline]
pub fn mutate_root<F, T>(&mut self, f: F) -> T
where
F: for<'gc> FnOnce(&'gc Mutation<'gc>, &'gc mut Root<'gc, R>) -> T,
{
self.context.root_barrier();
unsafe {
let mc: &'static Mutation<'_> = &*(self.context.mutation_context() as *const _);
let root: &'static mut Root<'_, R> = &mut *(&mut self.root as *mut _);
f(mc, root)
}
}
#[inline]
pub fn map_root<R2: for<'a> Rootable<'a>>(
self,
f: impl for<'gc> FnOnce(&'gc Mutation<'gc>, Root<'gc, R>) -> Root<'gc, R2>,
) -> Arena<R2> {
self.context.root_barrier();
let new_root: Root<'static, R2> = unsafe {
let mc: &'static Mutation<'_> = &*(self.context.mutation_context() as *const _);
f(mc, self.root)
};
Arena {
context: self.context,
root: new_root,
}
}
#[inline]
pub fn try_map_root<R2: for<'a> Rootable<'a>, E>(
self,
f: impl for<'gc> FnOnce(&'gc Mutation<'gc>, Root<'gc, R>) -> Result<Root<'gc, R2>, E>,
) -> Result<Arena<R2>, E> {
self.context.root_barrier();
let new_root: Root<'static, R2> = unsafe {
let mc: &'static Mutation<'_> = &*(self.context.mutation_context() as *const _);
f(mc, self.root)?
};
Ok(Arena {
context: self.context,
root: new_root,
})
}
#[inline]
pub fn total_allocated(&self) -> usize {
self.context.total_allocated()
}
#[inline]
pub fn remembered_size(&self) -> usize {
self.context.last_remembered_size()
}
#[inline]
pub fn allocation_debt(&self) -> f64 {
self.context.allocation_debt()
}
#[inline]
pub fn collect_debt(&mut self) {
unsafe {
let debt = self.context.allocation_debt();
if debt > 0.0 {
self.context.do_collection(&self.root, debt);
}
}
}
#[inline]
pub fn collect_all(&mut self) {
self.context.wake();
unsafe {
self.context.do_collection(&self.root, f64::INFINITY);
}
}
}
pub fn rootless_arena<F, R>(f: F) -> R
where
F: for<'gc> FnOnce(&'gc Mutation<'gc>) -> R,
{
unsafe {
let context = Context::new(ArenaParameters::default());
f(context.mutation_context())
}
}