use crate::Tracer;
use crate::heap::{GcOptions, Heap};
use crate::trace::Trace;
use std::cell::Cell;
use std::ops::Deref;
use std::pin::Pin;
use std::ptr;
use std::sync::Arc;
thread_local! {
static CURRENT_CTX: Cell<*const GcContextInner> = const { Cell::new(ptr::null()) };
}
fn set_current_context(ctx: &Pin<Box<GcContextInner>>) {
let target_ptr: *const GcContextInner = ctx.as_ref().get_ref();
CURRENT_CTX.with(|tls| {
if !tls.get().is_null() {
panic!("A GcContext is already set for this thread");
}
tls.set(target_ptr);
});
}
fn reset_current_context(ctx: &Pin<Box<GcContextInner>>) {
let target_ptr: *const GcContextInner = ctx.as_ref().get_ref();
CURRENT_CTX.with(|tls| {
if tls.get().addr() == target_ptr.addr() {
tls.set(ptr::null());
}
});
}
pub(crate) fn with_current_context(f: impl FnOnce(&GcContextInner)) -> bool {
CURRENT_CTX.with(|tls| {
let ctx_ptr = tls.get();
if ctx_ptr.is_null() {
false
} else {
let ctx = unsafe { &*ctx_ptr };
f(ctx);
true
}
})
}
pub(crate) struct GcContextInner {
pub heap: Arc<Heap>,
pub local_gray: Tracer,
_marker: std::marker::PhantomData<*const ()>, }
pub struct GcContext(Pin<Box<GcContextInner>>);
impl Default for GcContext {
fn default() -> Self {
Self::new()
}
}
impl GcContext {
pub fn new() -> Self {
let heap = Heap::new();
Self::with_heap(heap)
}
pub fn off() -> Self {
let heap = Heap::off();
Self::with_heap(heap)
}
pub fn with_options(options: GcOptions) -> Self {
let heap = Heap::with_options(options);
Self::with_heap(heap)
}
pub fn with_heap(heap: Arc<Heap>) -> Self {
let inner = Box::pin(GcContextInner {
heap,
local_gray: Tracer::new(),
_marker: std::marker::PhantomData,
});
set_current_context(&inner);
GcContext(inner)
}
pub fn allocate<T: Trace>(&self, data: T) -> crate::GcRoot<T> {
self.0.heap.allocate(data)
}
pub fn heap(&self) -> &Arc<Heap> {
&self.0.heap
}
}
impl Drop for GcContext {
fn drop(&mut self) {
reset_current_context(&self.0);
}
}
impl Deref for GcContext {
type Target = Arc<Heap>;
#[inline]
fn deref(&self) -> &Arc<Heap> {
&self.0.heap
}
}