sphinx/runtime/gc/
handle.rs1use 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
25pub 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 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 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 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 debug_assert!(deref_safe());
195 let gcbox = unsafe { ptr.as_ref() };
196 gcbox.value()
197 })
198 }
199
200 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 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 #[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); }
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); }
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); }
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); }
302}