#[cfg(not(feature = "no-gc"))]
use crate::env::dynamics;
use crate::env::env::Env;
#[cfg(not(feature = "no-gc"))]
use crate::env::env::GlobalEnv;
use std::cell::RefCell;
type StwReclaimHook = Box<dyn Fn() + Send + Sync + 'static>;
static STW_RECLAIM_HOOKS: std::sync::RwLock<Vec<StwReclaimHook>> =
std::sync::RwLock::new(Vec::new());
pub fn set_stw_reclaim_hook(f: impl Fn() + Send + Sync + 'static) {
STW_RECLAIM_HOOKS.write().unwrap().push(Box::new(f));
}
#[cfg(not(feature = "no-gc"))]
fn run_stw_reclaim() {
for hook in STW_RECLAIM_HOOKS.read().unwrap().iter() {
hook();
}
}
thread_local! {
static ENV_ROOTS: RefCell<Vec<*const Env>> = const { RefCell::new(Vec::new()) };
static VALUE_ROOTS: RefCell<Vec<(*const cljrs_value::Value, usize)>> =
const { RefCell::new(Vec::new()) };
static OPTION_VALUE_ROOTS: RefCell<Vec<(*const Option<cljrs_value::Value>, usize)>> =
const { RefCell::new(Vec::new()) };
}
pub struct EnvRootGuard;
impl Drop for EnvRootGuard {
fn drop(&mut self) {
ENV_ROOTS.with(|roots| {
roots.borrow_mut().pop();
});
}
}
pub struct ValueRootGuard {
pushed: bool,
}
impl Drop for ValueRootGuard {
fn drop(&mut self) {
if self.pushed {
VALUE_ROOTS.with(|roots| {
roots.borrow_mut().pop();
});
}
}
}
pub fn push_env_root(env: &Env) -> EnvRootGuard {
ENV_ROOTS.with(|roots| {
roots.borrow_mut().push(env as *const Env);
});
EnvRootGuard
}
pub fn root_value(val: &cljrs_value::Value) -> ValueRootGuard {
VALUE_ROOTS.with(|roots| {
roots
.borrow_mut()
.push((val as *const cljrs_value::Value, 1));
});
ValueRootGuard { pushed: true }
}
pub fn root_values(vals: &[cljrs_value::Value]) -> ValueRootGuard {
if vals.is_empty() {
return ValueRootGuard { pushed: false };
}
VALUE_ROOTS.with(|roots| {
roots.borrow_mut().push((vals.as_ptr(), vals.len()));
});
ValueRootGuard { pushed: true }
}
pub struct OptionValueRootGuard {
pushed: bool,
}
impl Drop for OptionValueRootGuard {
fn drop(&mut self) {
if self.pushed {
OPTION_VALUE_ROOTS.with(|roots| {
roots.borrow_mut().pop();
});
}
}
}
pub fn root_option_values(vals: &[Option<cljrs_value::Value>]) -> OptionValueRootGuard {
if vals.is_empty() {
return OptionValueRootGuard { pushed: false };
}
OPTION_VALUE_ROOTS.with(|roots| {
roots.borrow_mut().push((vals.as_ptr(), vals.len()));
});
OptionValueRootGuard { pushed: true }
}
#[cfg(feature = "no-gc")]
pub fn force_collect(_env: &Env) {}
#[cfg(not(feature = "no-gc"))]
pub fn force_collect(env: &Env) {
let Some(_stw_guard) = cljrs_gc::begin_stw() else {
cljrs_gc::safepoint();
return;
};
cljrs_gc::HEAP.collect(|visitor| {
cljrs_gc::HEAP.trace_registered_roots(visitor);
trace_env_roots(env, visitor);
trace_thread_env_roots(visitor);
trace_value_roots(visitor);
trace_option_value_roots(visitor);
dynamics::trace_current(visitor);
crate::env::taps::trace_roots(visitor);
cljrs_gc::trace_thread_alloc_roots(visitor);
});
run_stw_reclaim();
}
#[cfg(feature = "no-gc")]
pub fn gc_safepoint(_env: &Env) {}
#[cfg(not(feature = "no-gc"))]
pub fn gc_safepoint(env: &Env) {
if !cljrs_gc::gc_requested() && !cljrs_gc::CONFIG_CANCELLATION.in_progress() {
return;
}
if cljrs_gc::CONFIG_CANCELLATION.in_progress() {
cljrs_gc::safepoint();
return;
}
if !cljrs_gc::take_gc_request() {
cljrs_gc::safepoint();
return;
}
let Some(_stw_guard) = cljrs_gc::begin_stw() else {
cljrs_gc::safepoint();
return;
};
cljrs_gc::HEAP.collect(|visitor| {
cljrs_gc::HEAP.trace_registered_roots(visitor);
trace_env_roots(env, visitor);
trace_thread_env_roots(visitor);
trace_value_roots(visitor);
trace_option_value_roots(visitor);
dynamics::trace_current(visitor);
crate::env::taps::trace_roots(visitor);
cljrs_gc::trace_thread_alloc_roots(visitor);
});
run_stw_reclaim();
}
#[cfg(not(feature = "no-gc"))]
fn trace_env_roots(env: &Env, visitor: &mut cljrs_gc::MarkVisitor) {
use cljrs_gc::Trace;
for frame in &env.frames {
for (_name, val) in &frame.bindings {
val.trace(visitor);
}
}
trace_globals(&env.globals, visitor);
}
#[cfg(not(feature = "no-gc"))]
fn trace_value_roots(visitor: &mut cljrs_gc::MarkVisitor) {
use cljrs_gc::Trace;
VALUE_ROOTS.with(|roots| {
for &(ptr, count) in roots.borrow().iter() {
let slice = unsafe { std::slice::from_raw_parts(ptr, count) };
for val in slice {
val.trace(visitor);
}
}
});
}
#[cfg(not(feature = "no-gc"))]
fn trace_option_value_roots(visitor: &mut cljrs_gc::MarkVisitor) {
use cljrs_gc::Trace;
OPTION_VALUE_ROOTS.with(|roots| {
for &(ptr, count) in roots.borrow().iter() {
let slice = unsafe { std::slice::from_raw_parts(ptr, count) };
for val in slice.iter().flatten() {
val.trace(visitor);
}
}
});
}
#[cfg(not(feature = "no-gc"))]
fn trace_thread_env_roots(visitor: &mut cljrs_gc::MarkVisitor) {
use cljrs_gc::Trace;
ENV_ROOTS.with(|roots| {
for env_ptr in roots.borrow().iter() {
let env = unsafe { &**env_ptr };
for frame in &env.frames {
for (_name, val) in &frame.bindings {
val.trace(visitor);
}
}
}
});
}
#[cfg(not(feature = "no-gc"))]
fn trace_globals(globals: &GlobalEnv, visitor: &mut cljrs_gc::MarkVisitor) {
use cljrs_gc::{GcVisitor as _, Trace};
let namespaces = globals.namespaces.read().unwrap();
for ns_ptr in namespaces.values() {
visitor.visit(ns_ptr);
}
drop(namespaces);
let version_cache = globals.version_cache.lock().unwrap();
for val in version_cache.values() {
val.trace(visitor);
}
}
#[cfg(feature = "no-gc")]
pub fn async_gc_collect() {}
#[cfg(not(feature = "no-gc"))]
pub fn async_gc_collect() {
if !cljrs_gc::gc_requested() && !cljrs_gc::CONFIG_CANCELLATION.in_progress() {
return;
}
if cljrs_gc::CONFIG_CANCELLATION.in_progress() {
cljrs_gc::safepoint();
return;
}
if !cljrs_gc::take_gc_request() {
cljrs_gc::safepoint();
return;
}
let Some(_stw_guard) = cljrs_gc::begin_stw() else {
cljrs_gc::safepoint();
return;
};
cljrs_gc::HEAP.collect(|visitor| {
cljrs_gc::HEAP.trace_registered_roots(visitor);
trace_thread_env_roots(visitor);
trace_value_roots(visitor);
trace_option_value_roots(visitor);
dynamics::trace_current(visitor);
crate::env::taps::trace_roots(visitor);
cljrs_gc::trace_thread_alloc_roots(visitor);
});
run_stw_reclaim();
}