use std::cell::RefCell;
use crate::region::Region;
use crate::static_arena::static_arena;
use crate::{GcPtr, Trace};
pub(crate) enum AllocCtx {
Static,
Region(*mut Region),
Invocation(*mut Region),
}
unsafe impl Send for AllocCtx {}
thread_local! {
pub(crate) static ALLOC_CTX: RefCell<Vec<AllocCtx>> = const { RefCell::new(Vec::new()) };
}
pub(crate) fn alloc_in_ctx<T: Trace + 'static>(value: T) -> GcPtr<T> {
ALLOC_CTX.with(|ctx| {
let ctx = ctx.borrow();
match ctx.last() {
None | Some(AllocCtx::Static) => static_arena().alloc(value),
Some(AllocCtx::Region(ptr) | AllocCtx::Invocation(ptr)) => {
unsafe { &mut **ptr }.alloc(value)
}
}
})
}
pub fn invocation_is_active() -> bool {
ALLOC_CTX.with(|ctx| {
ctx.borrow()
.iter()
.any(|entry| matches!(entry, AllocCtx::Invocation(_)))
})
}
pub struct InvocationGuard {
region: Box<Region>,
in_ctx: bool,
}
impl InvocationGuard {
pub fn new(byte_limit: usize) -> Self {
assert!(
!invocation_is_active(),
"nested isolated allocation invocations are not supported"
);
let mut region = Box::new(Region::with_limit(byte_limit));
let ptr = region.as_mut() as *mut Region;
ALLOC_CTX.with(|ctx| ctx.borrow_mut().push(AllocCtx::Invocation(ptr)));
Self {
region,
in_ctx: true,
}
}
pub fn accounted_bytes(&self) -> usize {
self.region.accounted_bytes()
}
pub fn object_count(&self) -> usize {
self.region.object_count()
}
}
impl Drop for InvocationGuard {
fn drop(&mut self) {
if self.in_ctx {
ALLOC_CTX.with(|ctx| {
let popped = ctx.borrow_mut().pop();
debug_assert!(matches!(popped, Some(AllocCtx::Invocation(_))));
});
self.in_ctx = false;
}
self.region.reset();
}
}
pub struct ScratchGuard {
region: Option<Box<Region>>,
in_ctx: bool,
}
impl ScratchGuard {
pub fn new() -> Self {
if invocation_is_active() {
return Self {
region: None,
in_ctx: false,
};
}
let mut region = Box::new(Region::new());
let ptr = region.as_mut() as *mut Region;
ALLOC_CTX.with(|ctx| ctx.borrow_mut().push(AllocCtx::Region(ptr)));
Self {
region: Some(region),
in_ctx: true,
}
}
pub fn pop_for_return(&mut self) {
if self.in_ctx {
ALLOC_CTX.with(|ctx| ctx.borrow_mut().pop());
self.in_ctx = false;
}
}
}
impl Default for ScratchGuard {
fn default() -> Self {
Self::new()
}
}
impl Drop for ScratchGuard {
fn drop(&mut self) {
if self.in_ctx {
ALLOC_CTX.with(|ctx| ctx.borrow_mut().pop());
}
if let Some(region) = &mut self.region {
region.reset();
}
}
}
pub struct StaticCtxGuard {
pushed: bool,
}
impl StaticCtxGuard {
pub fn new() -> Self {
if invocation_is_active() {
return Self { pushed: false };
}
ALLOC_CTX.with(|ctx| ctx.borrow_mut().push(AllocCtx::Static));
Self { pushed: true }
}
}
impl Default for StaticCtxGuard {
fn default() -> Self {
Self::new()
}
}
impl Drop for StaticCtxGuard {
fn drop(&mut self) {
if self.pushed {
ALLOC_CTX.with(|ctx| ctx.borrow_mut().pop());
}
}
}