scheme_rs/gc/
mod.rs

1//! Garbage-Collected smart pointers with interior mutability.
2//!
3//! Gc<T> is conceptually similar to Arc<tokio::sync::RwLock<T>>, but garbage
4//! collection occurs concurrently at a fixed cadence or whenever a threshold
5//! of memory has been allocated as opposed to when the type is Dropped.
6//!
7//! Strictly speaking, Gc<T> is not garbage collection per-se but instead uses
8//! "cycle collection".
9//!
10//! Cycle collection was chosen because it has similar characteristics to Gc,
11//! providing all of the semantics Scheme expects and also plays nicely as a
12//! Rust type (no need to root/unroot).
13
14mod collection;
15
16use collection::{dec_rc, inc_rc};
17pub use collection::{init_gc, process_mutation_buffer};
18use futures::future::Shared;
19pub use scheme_rs_macros::Trace;
20
21use std::{
22    cell::UnsafeCell,
23    collections::{BTreeMap, BTreeSet, HashMap, HashSet},
24    future::Future,
25    marker::PhantomData,
26    ops::{Deref, DerefMut},
27    ptr::{drop_in_place, NonNull},
28    sync::Arc,
29};
30use tokio::sync::{RwLock, Semaphore, SemaphorePermit};
31
32/// A Garbage-Collected smart pointer with interior mutability.
33pub struct Gc<T: Trace> {
34    ptr: NonNull<GcInner<T>>,
35    marker: PhantomData<Arc<RwLock<T>>>,
36}
37
38impl<T: Trace> Gc<T> {
39    pub fn new(data: T) -> Gc<T> {
40        Self {
41            ptr: NonNull::from(Box::leak(Box::new(GcInner {
42                header: UnsafeCell::new(GcHeader::default()),
43                data: UnsafeCell::new(data),
44            }))),
45            marker: PhantomData,
46        }
47    }
48}
49
50impl<T: Trace> Gc<T> {
51    /// # Safety
52    ///
53    /// This function is not safe and basically useless for anything outside of
54    /// the Trace proc macro's generated code.
55    pub unsafe fn as_opaque(&self) -> OpaqueGcPtr {
56        self.ptr as OpaqueGcPtr
57    }
58
59    /// Acquire a read lock for the object
60    pub async fn read(&self) -> GcReadGuard<'_, T> {
61        unsafe {
62            let _permit = (*self.ptr.as_ref().header.get())
63                .semaphore
64                .acquire()
65                .await
66                .unwrap();
67            let data = &*self.ptr.as_ref().data.get() as *const T;
68            GcReadGuard {
69                _permit,
70                data,
71                marker: PhantomData,
72            }
73        }
74    }
75
76    /// Attempt to acquire a read lock for the object
77    pub fn try_read(&self) -> Option<GcReadGuard<'_, T>> {
78        unsafe {
79            let _permit = (*self.ptr.as_ref().header.get())
80                .semaphore
81                .try_acquire()
82                .ok()?;
83            let data = &*self.ptr.as_ref().data.get() as *const T;
84            Some(GcReadGuard {
85                _permit,
86                data,
87                marker: PhantomData,
88            })
89        }
90    }
91
92    /// Acquire a write lock for the object
93    pub async fn write(&self) -> GcWriteGuard<'_, T> {
94        unsafe {
95            let _permit = (*self.ptr.as_ref().header.get())
96                .semaphore
97                .acquire_many(MAX_READS)
98                .await
99                .unwrap();
100            let data = &mut *self.ptr.as_ref().data.get() as *mut T;
101            GcWriteGuard {
102                _permit,
103                data,
104                marker: PhantomData,
105            }
106        }
107    }
108}
109
110impl<T: Trace> std::fmt::Debug for Gc<T>
111where
112    T: std::fmt::Debug,
113{
114    fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
115        loop {
116            if let Some(x) = self.try_read() {
117                return x.fmt(fmt);
118            }
119        }
120    }
121}
122
123impl<T: Trace> Clone for Gc<T> {
124    fn clone(&self) -> Gc<T> {
125        inc_rc(self.ptr);
126        Self {
127            ptr: self.ptr,
128            marker: PhantomData,
129        }
130    }
131}
132
133impl<T: Trace> Drop for Gc<T> {
134    fn drop(&mut self) {
135        dec_rc(self.ptr);
136    }
137}
138
139unsafe impl<T: Trace + Send + Sync> Send for Gc<T> {}
140unsafe impl<T: Trace + Send + Sync> Sync for Gc<T> {}
141
142#[derive(Copy, Clone, Debug, PartialEq, Eq)]
143enum Color {
144    /// In use or free
145    Black,
146    /// Possible member of a cycle
147    Gray,
148    /// Member of a garbage cycle
149    White,
150    /// Possible root of cycle
151    Purple,
152    /// Candidate cycle undergoing Σ-computation
153    Red,
154    /// Candidate cycle awaiting epoch boundary
155    Orange,
156}
157
158const MAX_READS: u32 = u32::MAX >> 3;
159
160#[derive(derive_more::Debug)]
161pub struct GcHeader {
162    rc: usize,
163    crc: isize,
164    color: Color,
165    buffered: bool,
166    #[debug(skip)]
167    semaphore: Semaphore,
168}
169
170impl Default for GcHeader {
171    fn default() -> Self {
172        Self {
173            rc: 1,
174            crc: 1,
175            color: Color::Black,
176            buffered: false,
177            semaphore: Semaphore::new(MAX_READS as usize),
178        }
179    }
180}
181
182unsafe impl Send for GcHeader {}
183unsafe impl Sync for GcHeader {}
184
185pub struct GcInner<T: ?Sized> {
186    header: UnsafeCell<GcHeader>,
187    data: UnsafeCell<T>,
188}
189
190unsafe impl<T: ?Sized + Send + Sync> Send for GcInner<T> {}
191unsafe impl<T: ?Sized + Send + Sync> Sync for GcInner<T> {}
192
193type OpaqueGc = GcInner<dyn Trace>;
194pub type OpaqueGcPtr = NonNull<OpaqueGc>;
195
196pub struct GcReadGuard<'a, T: ?Sized> {
197    _permit: SemaphorePermit<'a>,
198    data: *const T,
199    marker: PhantomData<&'a T>,
200}
201
202impl<T: ?Sized> Deref for GcReadGuard<'_, T> {
203    type Target = T;
204
205    fn deref(&self) -> &T {
206        unsafe { &*self.data }
207    }
208}
209
210impl<T: ?Sized> AsRef<T> for GcReadGuard<'_, T> {
211    fn as_ref(&self) -> &T {
212        self
213    }
214}
215
216unsafe impl<T: ?Sized + Send + Sync> Send for GcReadGuard<'_, T> {}
217unsafe impl<T: ?Sized + Send + Sync> Sync for GcReadGuard<'_, T> {}
218
219pub struct GcWriteGuard<'a, T: ?Sized> {
220    _permit: SemaphorePermit<'a>,
221    data: *mut T,
222    marker: PhantomData<&'a mut T>,
223}
224
225impl<T: ?Sized> Deref for GcWriteGuard<'_, T> {
226    type Target = T;
227
228    fn deref(&self) -> &T {
229        unsafe { &*self.data }
230    }
231}
232
233impl<T: ?Sized> DerefMut for GcWriteGuard<'_, T> {
234    fn deref_mut(&mut self) -> &mut T {
235        unsafe { &mut *self.data }
236    }
237}
238
239/// # Safety
240///
241/// This trait should _not_ be manually implemented!
242pub unsafe trait Trace: 'static {
243    /// # Safety
244    ///
245    /// This function may _ONLY_ be called by the garbage collector! Calling this
246    /// function **ANYWHERE ELSE** is a **RACE CONDITION**!
247    ///
248    /// **DO NOT CALL THIS FUNCTION!!**
249    // TODO(map): Make this function async
250    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr));
251
252    /// # Safety
253    ///
254    /// This function may _ONLY_ be called by the garbage collector! Calling this
255    /// function **ANYWHERE ELSE** is a **RACE CONDITION**!
256    ///
257    /// **DO NOT CALL THIS FUNCTION!!**
258    unsafe fn finalize(&mut self) {
259        drop_in_place(self as *mut Self);
260    }
261}
262
263macro_rules! impl_empty_trace {
264    ( $( $x:ty ),* ) => {
265        $(
266            unsafe impl Trace for $x {
267                unsafe fn visit_children(&self, _visitor: fn(OpaqueGcPtr)) {}
268            }
269        )*
270    }
271}
272
273impl_empty_trace! {
274    (),
275    bool,
276    char,
277    f32,
278    f64,
279    // fn
280    i8,
281    i16,
282    i32,
283    i64,
284    i128,
285    isize,
286    // pointer
287    // reference
288    // slice,
289    // tuple
290    u8,
291    u16,
292    u32,
293    u64,
294    u128,
295    usize,
296    &'static str,
297    String
298}
299
300/// # Safety
301///
302/// This function is _not_ safe to implement!
303unsafe trait GcOrTrace: 'static {
304    unsafe fn visit_or_recurse(&self, visitor: fn(OpaqueGcPtr));
305
306    unsafe fn finalize_or_skip(&mut self);
307}
308
309unsafe impl<T: Trace> GcOrTrace for Gc<T> {
310    unsafe fn visit_or_recurse(&self, visitor: fn(OpaqueGcPtr)) {
311        visitor(self.as_opaque())
312    }
313
314    unsafe fn finalize_or_skip(&mut self) {}
315}
316
317unsafe impl<T: Trace + ?Sized> GcOrTrace for T {
318    unsafe fn visit_or_recurse(&self, visitor: fn(OpaqueGcPtr)) {
319        self.visit_children(visitor);
320    }
321
322    unsafe fn finalize_or_skip(&mut self) {
323        self.finalize();
324    }
325}
326
327unsafe impl<A, B> Trace for (A, B)
328where
329    A: GcOrTrace,
330    B: GcOrTrace,
331{
332    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
333        self.0.visit_or_recurse(visitor);
334        self.1.visit_or_recurse(visitor);
335    }
336
337    unsafe fn finalize(&mut self) {
338        self.0.finalize_or_skip();
339        self.1.finalize_or_skip();
340    }
341}
342
343unsafe impl<T> Trace for Vec<T>
344where
345    T: GcOrTrace,
346{
347    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
348        for child in self {
349            child.visit_or_recurse(visitor);
350        }
351    }
352
353    unsafe fn finalize(&mut self) {
354        for mut child in std::mem::take(self).into_iter() {
355            child.finalize_or_skip();
356            std::mem::forget(child);
357        }
358    }
359}
360
361unsafe impl<K> Trace for HashSet<K>
362where
363    K: GcOrTrace,
364{
365    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
366        for k in self {
367            k.visit_or_recurse(visitor);
368        }
369    }
370
371    unsafe fn finalize(&mut self) {
372        for mut k in std::mem::take(self).into_iter() {
373            k.finalize_or_skip();
374            std::mem::forget(k);
375        }
376    }
377}
378
379unsafe impl<K, V> Trace for HashMap<K, V>
380where
381    K: GcOrTrace,
382    V: GcOrTrace,
383{
384    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
385        for (k, v) in self {
386            k.visit_or_recurse(visitor);
387            v.visit_or_recurse(visitor);
388        }
389    }
390
391    unsafe fn finalize(&mut self) {
392        for (mut k, mut v) in std::mem::take(self) {
393            k.finalize_or_skip();
394            v.finalize_or_skip();
395            std::mem::forget((k, v));
396        }
397    }
398}
399
400unsafe impl<K> Trace for BTreeSet<K>
401where
402    K: GcOrTrace,
403{
404    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
405        for k in self {
406            k.visit_or_recurse(visitor);
407        }
408    }
409
410    unsafe fn finalize(&mut self) {
411        for mut k in std::mem::take(self) {
412            k.finalize_or_skip();
413            std::mem::forget(k);
414        }
415    }
416}
417
418unsafe impl<K, V> Trace for BTreeMap<K, V>
419where
420    K: GcOrTrace,
421    V: GcOrTrace,
422{
423    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
424        for (k, v) in self {
425            k.visit_or_recurse(visitor);
426            v.visit_or_recurse(visitor);
427        }
428    }
429
430    unsafe fn finalize(&mut self) {
431        for (mut k, mut v) in std::mem::take(self).into_iter() {
432            k.finalize_or_skip();
433            v.finalize_or_skip();
434            std::mem::forget((k, v));
435        }
436    }
437}
438
439unsafe impl<T> Trace for Option<T>
440where
441    T: GcOrTrace,
442{
443    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
444        if let Some(inner) = self {
445            inner.visit_or_recurse(visitor);
446        }
447    }
448
449    unsafe fn finalize(&mut self) {
450        if let Some(inner) = self {
451            inner.finalize_or_skip();
452        }
453    }
454}
455
456unsafe impl<T> Trace for Box<T>
457where
458    T: GcOrTrace,
459{
460    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
461        self.as_ref().visit_or_recurse(visitor);
462    }
463
464    unsafe fn finalize(&mut self) {
465        self.as_mut().finalize_or_skip();
466        todo!("need to dealloc data without dropping box");
467    }
468}
469
470unsafe impl<T> Trace for [T]
471where
472    T: GcOrTrace,
473{
474    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
475        for item in self {
476            item.visit_or_recurse(visitor);
477        }
478    }
479
480    unsafe fn finalize(&mut self) {
481        for item in self {
482            item.finalize_or_skip();
483        }
484    }
485}
486
487unsafe impl<T> Trace for std::sync::Arc<T>
488where
489    T: ?Sized + 'static,
490{
491    unsafe fn visit_children(&self, _visitor: fn(OpaqueGcPtr)) {
492        // We cannot visit the children for an Arc, as it may lead to situations
493        // were we incorrectly decrement a child twice.
494        // An Arc wrapping a Gc effectively creates an additional ref count for
495        // that Gc that we cannot access.
496    }
497}
498
499unsafe impl<T> Trace for std::sync::Weak<T>
500where
501    T: ?Sized + 'static,
502{
503    unsafe fn visit_children(&self, _visitor: fn(OpaqueGcPtr)) {
504        // Same reasoning as Arc. If we're not visiting Arcs, we shouldn't visit Weak.
505        // Let it handle its own ref count.
506    }
507}
508
509unsafe impl<T> Trace for Shared<T>
510where
511    T: Future + 'static,
512{
513    unsafe fn visit_children(&self, _visitor: fn(OpaqueGcPtr)) {}
514}
515
516unsafe impl<A: 'static, O: 'static> Trace for fn(A) -> O {
517    unsafe fn visit_children(&self, _visitor: fn(OpaqueGcPtr)) {}
518}
519
520unsafe impl<A: 'static, B: 'static, O: 'static> Trace for fn(A, B) -> O {
521    unsafe fn visit_children(&self, _visitor: fn(OpaqueGcPtr)) {}
522}
523
524unsafe impl<T> Trace for tokio::sync::Mutex<T>
525where
526    T: GcOrTrace,
527{
528    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
529        // TODO: Think really hard as to if this is correct
530        // This _should_ be fine, while not optimally efficient.
531        let lock = self.blocking_lock();
532        lock.visit_or_recurse(visitor);
533    }
534}
535
536unsafe impl<T> Trace for tokio::sync::RwLock<T>
537where
538    T: GcOrTrace,
539{
540    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
541        // TODO: Think really hard as to if this is correct
542        loop {
543            if let Ok(read_lock) = self.try_read() {
544                read_lock.visit_or_recurse(visitor);
545                return;
546            }
547        }
548    }
549}
550
551unsafe impl<T> Trace for std::sync::Mutex<T>
552where
553    T: GcOrTrace,
554{
555    unsafe fn visit_children(&self, visitor: fn(OpaqueGcPtr)) {
556        // TODO: Think really hard as to if this is correct
557        let lock = self.lock().unwrap();
558        lock.visit_or_recurse(visitor);
559    }
560}