use std::{
marker::PhantomData,
panic::{AssertUnwindSafe, catch_unwind, resume_unwind},
};
pub use jl_sys::GcCollection;
use jl_sys::{
jl_gc_collect, jl_gc_collection_t, jl_gc_enable, jl_gc_is_enabled, jl_gc_mark_queue_obj,
jl_gc_mark_queue_objarray, jl_gc_safepoint, jl_get_pgcstack,
};
use jlrs_sys::{
jlrs_gc_safe_enter, jlrs_gc_safe_leave, jlrs_gc_unsafe_enter, jlrs_gc_unsafe_leave, jlrs_gc_wb,
};
use super::{
PTls, get_tls,
target::{Target, unrooted::Unrooted},
};
use crate::{
call::Call,
data::managed::{
module::Module,
private::ManagedPriv,
value::{Value, WeakValue},
},
private::Private,
};
pub struct GcInterface<H>(PhantomData<H>);
impl<'borrow, H> GcInterface<&'borrow H> {
#[inline(always)]
pub(crate) fn new(_: &'borrow H) -> Self {
GcInterface(PhantomData)
}
}
pub trait Gc: private::GcPriv {
#[inline]
fn enable_gc(&self, on: bool) -> bool {
unsafe { jl_gc_enable(on as i32) != 0 }
}
fn enable_gc_logging(&self, on: bool) {
let global = unsafe { Unrooted::new() };
let func = unsafe {
Module::base(&global)
.submodule(&global, "GC")
.expect("No GC module in Base")
.as_managed()
.global(&global, "enable_logging")
.expect("No enable_logging function in GC")
.as_managed()
};
let arg = if on {
Value::true_v(&global)
} else {
Value::false_v(&global)
};
unsafe { func.call(&global, [arg]) }.expect("GC.enable_logging threw an exception");
}
#[inline]
fn gc_is_enabled(&self) -> bool {
unsafe { jl_gc_is_enabled() != 0 }
}
#[inline]
fn gc_collect(&self, mode: GcCollection) {
unsafe { jl_gc_collect(mode as jl_gc_collection_t) }
}
#[inline]
fn gc_collect_n(&self, mode: GcCollection, n: usize) {
for _i in 0..n {
self.gc_collect(mode);
}
}
#[inline]
fn gc_safepoint(&self) {
unsafe {
jl_gc_safepoint();
}
}
#[inline]
unsafe fn gc_safe_enter() -> i8 {
unsafe {
let ptls = get_tls();
jlrs_gc_safe_enter(ptls)
}
}
#[inline]
unsafe fn gc_safe_leave(state: i8) {
unsafe {
let ptls = get_tls();
jlrs_gc_safe_leave(ptls, state)
}
}
#[inline]
unsafe fn gc_unsafe_enter() -> i8 {
unsafe {
let ptls = get_tls();
jlrs_gc_unsafe_enter(ptls)
}
}
#[inline]
unsafe fn gc_unsafe_leave(state: i8) {
unsafe {
let ptls = get_tls();
jlrs_gc_unsafe_leave(ptls, state)
}
}
}
#[inline]
pub unsafe fn mark_queue_obj(ptls: PTls, obj: WeakValue) -> bool {
unsafe { jl_gc_mark_queue_obj(ptls, obj.ptr().as_ptr()) != 0 }
}
#[inline]
pub unsafe fn mark_queue_objarray(ptls: PTls, parent: WeakValue, objs: &[Option<WeakValue>]) {
unsafe {
jl_gc_mark_queue_objarray(ptls, parent.ptr().as_ptr(), objs.as_ptr() as _, objs.len())
}
}
#[inline]
pub unsafe fn write_barrier<T>(data: &mut T, child: Value) {
unsafe { jlrs_gc_wb(data as *mut _ as *mut _, child.unwrap(Private).cast()) }
}
#[inline]
pub unsafe fn gc_safe<F: FnOnce() -> T, T>(f: F) -> T {
unsafe {
let pgc = jl_get_pgcstack();
if pgc.is_null() {
return f();
}
let ptls = get_tls();
let state = jlrs_gc_safe_enter(ptls);
let res = catch_unwind(AssertUnwindSafe(f));
jlrs_gc_safe_leave(ptls, state);
match res {
Ok(res) => res,
Err(e) => resume_unwind(e),
}
}
}
#[inline]
#[cfg(feature = "async")]
pub(crate) unsafe fn gc_safe_with<F: FnOnce() -> T, T>(ptls: PTls, f: F) -> T {
unsafe {
let state = jlrs_gc_safe_enter(ptls);
let res = catch_unwind(AssertUnwindSafe(f));
jlrs_gc_safe_leave(ptls, state);
match res {
Ok(res) => res,
Err(e) => resume_unwind(e),
}
}
}
#[inline]
pub unsafe fn gc_unsafe<F: for<'scope> FnOnce(Unrooted<'scope>) -> T, T>(f: F) -> T {
unsafe {
debug_assert!(!jl_get_pgcstack().is_null());
let ptls = get_tls();
let unrooted = Unrooted::new();
let state = jlrs_gc_unsafe_enter(ptls);
let res = catch_unwind(AssertUnwindSafe(|| f(unrooted)));
jlrs_gc_unsafe_leave(ptls, state);
match res {
Ok(res) => res,
Err(e) => resume_unwind(e),
}
}
}
#[cfg(feature = "async")]
pub(crate) unsafe fn gc_unsafe_with<F: for<'scope> FnOnce(Unrooted<'scope>) -> T, T>(
ptls: PTls,
f: F,
) -> T {
unsafe {
let state = jlrs_gc_unsafe_enter(ptls);
let unrooted = Unrooted::new();
let res = catch_unwind(AssertUnwindSafe(|| f(unrooted)));
jlrs_gc_unsafe_leave(ptls, state);
match res {
Ok(res) => res,
Err(e) => resume_unwind(e),
}
}
}
impl<'frame, Tgt: Target<'frame>> Gc for Tgt {}
impl<H> Gc for GcInterface<H> {}
mod private {
use super::GcInterface;
use crate::memory::target::Target;
pub trait GcPriv {}
impl<'frame, Tgt: Target<'frame>> GcPriv for Tgt {}
impl<H> GcPriv for GcInterface<H> {}
}