use alloc::boxed::Box;
use core::marker::PhantomData;
use crate::{
Collect,
context::{Context, Finalization, Mutation, Phase, RunUntil, Stop},
metrics::Metrics,
};
pub trait Rootable<'a> {
type Root: ?Sized + '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;
#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub enum CollectionPhase {
Sleeping,
Marking,
Marked,
Sweeping,
}
pub struct Arena<R>
where
R: for<'a> Rootable<'a>,
{
context: Box<Context>,
root: Root<'static, R>,
}
impl<R> Arena<R>
where
R: for<'a> Rootable<'a>,
for<'a> Root<'a, R>: Sized,
{
pub fn new<F>(f: F) -> Arena<R>
where
F: for<'gc> FnOnce(&'gc Mutation<'gc>) -> Root<'gc, R>,
{
unsafe {
let context = Box::new(Context::new());
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>(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());
let mc: &'static Mutation<'_> = &*(context.mutation_context() as *const _);
let root: Root<'static, R> = f(mc)?;
Ok(Arena { context, root })
}
}
#[inline]
pub fn map_root<R2>(
mut self,
f: impl for<'gc> FnOnce(&'gc Mutation<'gc>, Root<'gc, R>) -> Root<'gc, R2>,
) -> Arena<R2>
where
R2: for<'a> Rootable<'a>,
for<'a> Root<'a, R2>: Sized,
{
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, E>(
mut self,
f: impl for<'gc> FnOnce(&'gc Mutation<'gc>, Root<'gc, R>) -> Result<Root<'gc, R2>, E>,
) -> Result<Arena<R2>, E>
where
R2: for<'a> Rootable<'a>,
for<'a> Root<'a, R2>: Sized,
{
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,
})
}
}
impl<R> Arena<R>
where
R: for<'a> Rootable<'a>,
{
#[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 metrics(&self) -> &Metrics {
self.context.metrics()
}
#[inline]
pub fn collection_phase(&self) -> CollectionPhase {
match self.context.phase() {
Phase::Mark => {
if self.context.gray_remaining() {
CollectionPhase::Marking
} else {
CollectionPhase::Marked
}
}
Phase::Sweep => CollectionPhase::Sweeping,
Phase::Sleep => CollectionPhase::Sleeping,
Phase::Drop => unreachable!(),
}
}
}
impl<R> Arena<R>
where
R: for<'a> Rootable<'a>,
for<'a> Root<'a, R>: Collect<'a>,
{
#[inline]
pub fn collect_debt(&mut self) {
unsafe {
self.context
.do_collection(&self.root, RunUntil::PayDebt, Stop::Full);
}
}
#[inline]
pub fn mark_debt(&mut self) -> Option<MarkedArena<'_, R>> {
unsafe {
self.context
.do_collection(&self.root, RunUntil::PayDebt, Stop::FullyMarked);
}
if self.context.phase() == Phase::Mark && !self.context.gray_remaining() {
Some(MarkedArena(self))
} else {
None
}
}
#[inline]
pub fn finish_marking(&mut self) -> Option<MarkedArena<'_, R>> {
unsafe {
self.context
.do_collection(&self.root, RunUntil::Stop, Stop::FullyMarked);
}
if self.context.phase() == Phase::Mark && !self.context.gray_remaining() {
Some(MarkedArena(self))
} else {
None
}
}
#[inline]
pub fn cycle_debt(&mut self) {
unsafe {
self.context
.do_collection(&self.root, RunUntil::PayDebt, Stop::FinishCycle);
}
}
#[inline]
pub fn finish_cycle(&mut self) {
unsafe {
self.context
.do_collection(&self.root, RunUntil::Stop, Stop::FinishCycle);
}
}
}
pub struct MarkedArena<'a, R: for<'b> Rootable<'b>>(&'a mut Arena<R>);
impl<'a, R> MarkedArena<'a, R>
where
R: for<'b> Rootable<'b>,
for<'b> Root<'b, R>: Collect<'b>,
{
#[inline]
pub fn finalize<F, T>(self, f: F) -> T
where
F: for<'gc> FnOnce(&'gc Finalization<'gc>, &'gc Root<'gc, R>) -> T,
{
unsafe {
let mc: &'static Finalization<'_> =
&*(self.0.context.finalization_context() as *const _);
let root: &'static Root<'_, R> = &*(&self.0.root as *const _);
f(mc, root)
}
}
#[inline]
pub fn start_sweeping(self) {
unsafe {
self.0
.context
.do_collection(&self.0.root, RunUntil::Stop, Stop::AtSweep);
}
assert_eq!(self.0.context.phase(), Phase::Sweep);
}
}
pub fn rootless_mutate<F, R>(f: F) -> R
where
F: for<'gc> FnOnce(&'gc Mutation<'gc>) -> R,
{
unsafe {
let context = Context::new();
f(context.mutation_context())
}
}