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)
    }
}