1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149
use crate::Janet;
/// The Janet Garbage Collector type.
///
/// It allows the use of garbage collection operations in the Janet public C API.
#[derive(Debug, Default)]
pub struct JanetGc {
_phantom: core::marker::PhantomData<*const ()>,
}
impl JanetGc {
/// Obtain the [`JanetGc`].
#[inline]
#[must_use = "function is a constructor associated function"]
pub fn obtain() -> Self {
Self {
_phantom: core::marker::PhantomData,
}
}
/// Run the garbage collection if there is nothing locking or suspending the garbage
/// collector, like an active [`JanetGcLockGuard`] or a call to a Janet C API that
/// locks the GC.
///
/// If there is something locking the garbage collection, it simply does a no-op.
///
/// # Safety
/// This function will free all memory allocated with the [Janet scratch memory
/// API](crate::allocator::Scratch) and any [non-rooted](JanetGc::root) object
/// that have no reference to a live object (as example, an empty
/// [`JanetTable`](crate::JanetTable) or [`JanetArray`](crate::JanetArray) )
#[inline]
pub unsafe fn collect(&self) {
evil_janet::janet_collect()
}
/// Lock the Janet GC and suspend any garbage collection until the guard is dropped.
///
/// If there is any attempt to manually trigger the garbage collection while there is
/// a [`JanetGcLockGuard`] active (or any unsafe call to the Janet C API locking the
/// GC)
#[inline]
#[must_use = "JanetGcLockGuard will be dropped if the result is not used"]
pub fn lock(&self) -> JanetGcLockGuard {
let handle = unsafe { evil_janet::janet_gclock() };
JanetGcLockGuard::new(handle)
}
/// Immediately drops the guard, and consequently unlocks the Janet GC.
///
/// This function is equivalent to calling [`drop`] on the guard but is more
/// self-documenting. Alternately, the guard will be automatically dropped when it
/// goes out of scope.
///
/// # Example:
///
/// ```
/// # let _client = janetrs::client::JanetClient::init().unwrap();
/// use janetrs::JanetGc;
///
/// let gc = JanetGc::obtain();
///
/// let mut guard = gc.lock();
///
/// // do stuff with the Janet GC locked
///
/// JanetGc::unlock(guard);
/// ```
#[inline]
pub fn unlock(guard: JanetGcLockGuard) {
drop(guard)
}
/// Roots the `value` to the GC. This prevents the GC from removing the `value` and
/// all of its children in a garbage collection.
///
/// The returned guard will [`unroot`](JanetGc::unroot) the `value` when dropped.
#[inline]
#[must_use = "JanetGcRootGuard will be dropped if the result is not used"]
pub fn root(&self, value: Janet) -> JanetGcRootGuard {
JanetGcRootGuard::new(value)
}
/// Immediately drops the guard, and consequently unlocks the Janet GC.
///
/// This function is equivalent to calling [`drop`] on the guard but is more
/// self-documenting. Alternately, the guard will be automatically dropped when it
/// goes out of scope.
#[inline]
pub fn unroot(guard: JanetGcRootGuard) {
drop(guard)
}
}
/// An RAII implementation of a “scoped lock” of the Janet GC. When this structure is
/// dropped (falls out of scope), the lock will be unlocked.
#[derive(Debug)]
pub struct JanetGcLockGuard {
handle: i32,
_phantom: core::marker::PhantomData<*const ()>,
}
impl JanetGcLockGuard {
#[inline]
pub(crate) fn new(handle: i32) -> Self {
Self {
handle,
_phantom: core::marker::PhantomData,
}
}
}
impl Drop for JanetGcLockGuard {
#[inline]
fn drop(&mut self) {
unsafe { evil_janet::janet_gcunlock(self.handle) }
}
}
/// An RAII implementation of a rooted [`Janet`] value. When this structure is dropped
/// (falls out of scope), the rooting will be undone.
#[derive(Debug)]
pub struct JanetGcRootGuard {
value: Janet,
_phantom: core::marker::PhantomData<*const ()>,
}
impl JanetGcRootGuard {
#[inline]
fn new(value: Janet) -> Self {
unsafe { evil_janet::janet_gcroot(value.inner) };
Self {
value,
_phantom: core::marker::PhantomData,
}
}
}
impl Drop for JanetGcRootGuard {
#[inline]
fn drop(&mut self) {
// SAFETY: Since we unrooting the same value we rooted, this should always work.
// For extra safety, below it's add a debug assert to be sure on debug compilations.
let res = unsafe { evil_janet::janet_gcunroot(self.value.inner) };
// Assert in debug mode that the result is 1
debug_assert_eq!(res, 1)
}
}