sphinx/runtime/gc/
handle.rs

1///! The "public API" for the gc is the `Gc<T>` struct, which is a smart pointer to GCed data.
2///! Data can be "inserted" into the GC using `Gc::new()` for `Sized` types or `Gc::from_box()` for `?Sized` types.
3///! Data is accessed using `Deref`. 
4///! Mutable access is not supported, so mutable data that needs to be GCed must use interior mutability.
5///! As well, all GCed data must `impl GcTrace`.
6///! Weak references to GCed data can be obtained using `Gc::weakref()`.
7///! 
8///! `Gc<T>` supports a "thin pointer" representation and should not be wider than a single `usize`.
9
10use core::fmt;
11use core::ops::Deref;
12use core::borrow::Borrow;
13use core::ptr::{self, NonNull, Pointee};
14use core::hash::{Hash, Hasher};
15use core::marker::PhantomData;
16use std::rc::Rc;
17
18use crate::runtime::gc::{GC_STATE, deref_safe};
19use crate::runtime::gc::trace::GcTrace;
20use crate::runtime::gc::gcbox::{GcBox, GcBoxPtr};
21use crate::runtime::gc::ptrmeta::PtrMetadata;
22use crate::runtime::gc::weak::GcWeakCell;
23
24
25///! Smart pointer to GCed data. See the module-level documentation for more details.
26pub struct Gc<T> where T: GcTrace + ?Sized + 'static {
27    ptr: GcBoxPtr,
28    _marker: PhantomData<Rc<GcBox<T>>>,
29}
30
31impl<T: GcTrace> Gc<T> {
32    pub fn new(data: T) -> Self {
33        GC_STATE.with(|gc| {
34            let mut gc = gc.borrow_mut();
35            
36            let gcbox = GcBox::new(data);
37            gc.insert(gcbox);
38            Self::from_raw(gcbox)
39        })
40    }
41}
42
43impl<T> Gc<T> where 
44    T: GcTrace + ?Sized + Pointee, 
45    T::Metadata: Into<PtrMetadata>,
46    GcBox<T>: Pointee<Metadata = T::Metadata> 
47{
48    pub fn from_box(data: Box<T>) -> Self {
49        GC_STATE.with(|gc| {
50            let mut gc = gc.borrow_mut();
51            
52            let gcbox = GcBox::from_box(data);
53            gc.insert(gcbox);
54            Self::from_raw(gcbox)
55        })
56    }
57}
58
59impl<T> Gc<T> where T: GcTrace + ?Sized {
60    pub(super) fn from_raw(ptr: NonNull<GcBox<T>>) -> Self {
61        Self { 
62            ptr: ptr.into(),
63            _marker: PhantomData,
64        }
65    }
66    
67    pub(super) fn as_raw(self_gc: &Gc<T>) -> GcBoxPtr {
68        self_gc.ptr
69    }
70    
71    pub fn ptr_eq<U>(self_gc: &Gc<T>, other_gc: &Gc<U>) -> bool where U: GcTrace + ?Sized {
72        ptr::eq(
73            self_gc.ptr.as_ptr() as *const (),
74            other_gc.ptr.as_ptr() as *const (),
75        )
76    }
77    
78    /// Casts the inner pointer to a usize. 
79    /// This is intended for identifying the Gc, and should not be cast back to a pointer.
80    pub fn as_id(self_gc: &Gc<T>) -> usize {
81        self_gc.ptr.as_ptr() as *const () as usize
82    }
83}
84
85impl<T> Gc<T> where 
86    T: GcTrace + ?Sized, 
87    PtrMetadata: TryInto<<GcBox<T> as Pointee>::Metadata>
88{
89    #[inline]
90    fn inner(&self) -> &GcBox<T> {
91        // must not deref during sweep. This should only be possible if called inside a Drop impl
92        debug_assert!(deref_safe());
93        unsafe { self.ptr.to_gcbox_ptr().as_ref() }
94    }
95    
96    #[inline]
97    fn inner_mut(&mut self) -> &mut GcBox<T> {
98        debug_assert!(deref_safe());
99        unsafe { self.ptr.to_gcbox_ptr().as_mut() }
100    }
101    
102    /// Create a weak reference from this GC handle
103    pub fn weakref(&self) -> GcWeak<T> {
104        let weak_ptr = GcBox::get_or_make_weak(self.ptr.to_gcbox_ptr());
105        GcWeak::new(Gc::from_raw(weak_ptr))
106    }
107    
108    pub fn mark_trace(mut self) {
109        self.inner_mut().mark_trace()
110    }
111}
112
113impl<T> From<Gc<T>> for Gc<dyn GcTrace> where T: GcTrace {
114    fn from(handle: Gc<T>) -> Self {
115        Self {
116            ptr: handle.ptr,
117            _marker: PhantomData,
118        }
119    }
120}
121
122impl<T> AsRef<T> for Gc<T> where 
123    T: GcTrace + ?Sized,
124    PtrMetadata: TryInto<<GcBox<T> as Pointee>::Metadata>
125{
126    fn as_ref(&self) -> &T {
127        self.deref()
128    }
129}
130
131impl<T> Borrow<T> for Gc<T> where 
132    T: GcTrace + ?Sized,
133    PtrMetadata: TryInto<<GcBox<T> as Pointee>::Metadata>
134{
135    fn borrow(&self) -> &T {
136        self.deref()
137    }
138}
139
140impl<T> Deref for Gc<T> where 
141    T: GcTrace + ?Sized,
142    PtrMetadata: TryInto<<GcBox<T> as Pointee>::Metadata>
143{
144    type Target = T;
145    
146    #[inline]
147    fn deref(&self) -> &Self::Target {
148        self.inner().value()
149    }
150}
151
152impl<T> Clone for Gc<T> where T: GcTrace + ?Sized {
153    fn clone(&self) -> Self {
154        Self {
155            ptr: self.ptr,
156            _marker: PhantomData,
157        }
158    }
159}
160
161impl<T> Copy for Gc<T> where T: GcTrace + ?Sized { }
162
163impl<T> Hash for Gc<T> where T: GcTrace + ?Sized {
164    fn hash<H>(&self, state: &mut H) where H: Hasher {
165        self.ptr.hash(state)
166    }
167}
168
169impl<T> fmt::Debug for Gc<T> where T: GcTrace + ?Sized {
170    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
171        fmt.debug_tuple("Gc")
172            .field(&self.ptr.as_ptr())
173            .finish()
174    }
175}
176
177
178pub struct GcWeak<T> where T: GcTrace + ?Sized + 'static {
179    gc_weak: Gc<GcWeakCell<T>>,
180}
181
182impl<T> GcWeak<T> where T: GcTrace + ?Sized {
183    fn new(gc_weak: Gc<GcWeakCell<T>>) -> Self {
184        Self { gc_weak }
185    }
186    
187    pub fn is_valid(&self) -> bool {
188        self.gc_weak.get().is_some()
189    }
190    
191    pub fn try_deref(&self) -> Option<&T> {
192        self.gc_weak.get().map(|ptr| {
193            // must not deref during sweep. This should only be possible if called inside a Drop impl
194            debug_assert!(deref_safe());
195            let gcbox = unsafe { ptr.as_ref() };
196            gcbox.value()
197        })
198    }
199    
200    // This marks the allocation for the weak reference - NOT the referent of the weak reference
201    pub fn mark_trace(&self) {
202        self.gc_weak.mark_trace()
203    }
204    
205    pub fn ptr_eq<U>(self_weak: &GcWeak<T>, other_weak: &GcWeak<U>) -> bool where U: GcTrace + ?Sized {
206        Gc::ptr_eq(&self_weak.gc_weak, &other_weak.gc_weak)
207    }
208    
209    /// This is intended for identifying the GcWeak, and should not be cast back to a pointer.
210    pub fn as_id(self_weak: &GcWeak<T>) -> usize {
211        Gc::as_id(&self_weak.gc_weak)
212    }
213}
214
215impl<T> Clone for GcWeak<T> where T: GcTrace + ?Sized {
216    fn clone(&self) -> Self {
217        Self {
218            gc_weak: self.gc_weak.clone()
219        }
220    }
221}
222
223impl<T> Copy for GcWeak<T> where T: GcTrace + ?Sized { }
224
225impl<T> fmt::Debug for GcWeak<T> where T: GcTrace + ?Sized {
226    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
227        fmt.debug_tuple("GcWeak")
228            .field(&self.gc_weak.ptr.as_ptr())
229            .finish()
230    }
231}
232
233
234#[cfg(test)]
235mod tests {
236    use super::*;
237    use core::cell::Cell;
238    use crate::runtime::gc::gc_force;
239    use static_assertions::assert_eq_size;
240    
241    assert_eq_size!(Gc<i32>, usize);
242    assert_eq_size!(Gc<[i32]>, usize);
243    
244    // comment this out when using miri
245    // use test_log::test;
246    
247    #[test]
248    fn test_weak_ref_dereference() {
249        let data = Gc::new(1);
250        let weak = data.weakref();
251        
252        assert!(matches!(weak.try_deref(), Some(1)));
253        
254        gc_force(&0); //cleanup so miri doesn't complain about leaks
255    }
256    
257    #[test]
258    fn test_weak_ref_dereference_mut() {
259        let data = Gc::new(Cell::new(2));
260        let weak = data.weakref();
261        
262        let cell = weak.try_deref().unwrap();
263        assert!(cell.get() == 2);
264        
265        cell.set(cell.get() + 1);
266        assert!(cell.get() == 3);
267        
268        gc_force(&0); //cleanup so miri doesn't complain about leaks
269    }
270    
271    #[test]
272    fn test_weak_ref_invalidated() {
273        let data = Gc::new(3);
274        let weak = data.weakref();
275        assert!(weak.is_valid());
276        
277        weak.mark_trace();
278        
279        gc_force(&0);
280        
281        assert!(!weak.is_valid());
282        
283        gc_force(&0); //cleanup so miri doesn't complain about leaks
284    }
285    
286    #[test]
287    fn test_weak_ref_reclaimed() {
288        let data = Gc::new(4);
289        assert!(data.inner().header().weak().is_none());
290        
291        data.weakref();
292        assert!(data.inner().header().weak().is_some());
293        
294        data.mark_trace();
295        
296        gc_force(&0);
297        
298        assert!(data.inner().header().weak().is_none());
299        
300        gc_force(&0); //cleanup so miri doesn't complain about leaks
301    }
302}