gc_arena/
dynamic_roots.rs1use alloc::{
2 rc::{Rc, Weak},
3 vec::Vec,
4};
5use core::{fmt, mem};
6
7use crate::lock::RefLock;
8use crate::{
9 barrier::{unlock, Write},
10 metrics::Metrics,
11};
12use crate::{Collect, Gc, Mutation, Root, Rootable};
13
14#[derive(Copy, Clone)]
28pub struct DynamicRootSet<'gc>(Gc<'gc, Inner<'gc>>);
29
30unsafe impl<'gc> Collect for DynamicRootSet<'gc> {
31 fn trace(&self, cc: &crate::Collection) {
32 self.0.trace(cc);
33 }
34}
35
36impl<'gc> DynamicRootSet<'gc> {
37 pub fn new(mc: &Mutation<'gc>) -> Self {
39 DynamicRootSet(Gc::new(
40 mc,
41 Inner {
42 handles: RefLock::new(Vec::new()),
43 metrics: mc.metrics().clone(),
44 set_id: Rc::new(SetId {}),
45 },
46 ))
47 }
48
49 pub fn stash<R: for<'a> Rootable<'a>>(
54 &self,
55 mc: &Mutation<'gc>,
56 root: Root<'gc, R>,
57 ) -> DynamicRoot<R> {
58 let handle = Rc::new(Handle {
59 set_id: self.0.set_id.clone(),
60 root,
61 });
62
63 Inner::adopt_handle(&Gc::write(mc, self.0), &handle);
64
65 DynamicRoot {
66 handle: unsafe {
67 mem::transmute::<Rc<Handle<Root<'gc, R>>>, Rc<Handle<Root<'static, R>>>>(handle)
68 },
69 }
70 }
71
72 #[inline]
79 pub fn fetch<'a, R: for<'r> Rootable<'r>>(&self, root: &'a DynamicRoot<R>) -> &'a Root<'gc, R> {
80 if self.contains(root) {
81 unsafe { &*root.as_ptr() }
82 } else {
83 panic!("mismatched root set")
84 }
85 }
86
87 #[inline]
90 pub fn try_fetch<'a, R: for<'r> Rootable<'r>>(
91 &self,
92 root: &'a DynamicRoot<R>,
93 ) -> Result<&'a Root<'gc, R>, MismatchedRootSet> {
94 if self.contains(root) {
95 unsafe { Ok(&*root.as_ptr()) }
96 } else {
97 Err(MismatchedRootSet(()))
98 }
99 }
100
101 #[inline]
103 pub fn contains<R: for<'r> Rootable<'r>>(&self, root: &DynamicRoot<R>) -> bool {
104 let ours = Rc::as_ptr(&self.0.set_id);
105 let theirs = Rc::as_ptr(&root.handle.set_id);
106 ours == theirs
107 }
108}
109
110pub struct DynamicRoot<R: for<'gc> Rootable<'gc>> {
118 handle: Rc<Handle<Root<'static, R>>>,
119}
120
121impl<R: for<'gc> Rootable<'gc>> Clone for DynamicRoot<R> {
122 fn clone(&self) -> Self {
123 Self {
124 handle: self.handle.clone(),
125 }
126 }
127}
128
129impl<R: for<'gc> Rootable<'gc>> DynamicRoot<R> {
130 #[inline]
144 pub fn as_ptr<'gc>(&self) -> *const Root<'gc, R> {
145 unsafe { mem::transmute::<&Root<'static, R>, &Root<'gc, R>>(&self.handle.root) as *const _ }
146 }
147}
148
149#[derive(Debug)]
151pub struct MismatchedRootSet(());
152
153impl fmt::Display for MismatchedRootSet {
154 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
155 f.write_str("mismatched root set")
156 }
157}
158
159#[cfg(feature = "std")]
160impl std::error::Error for MismatchedRootSet {}
161
162struct SetId {}
164
165struct HandleStore<'gc> {
166 handle: Weak<Handle<dyn Collect + 'gc>>,
167 root_size: usize,
168}
169
170struct Inner<'gc> {
171 handles: RefLock<Vec<HandleStore<'gc>>>,
172 metrics: Metrics,
173 set_id: Rc<SetId>,
174}
175
176fn size_of_handle_list<'gc>(list: &Vec<HandleStore<'gc>>) -> usize {
177 list.capacity() * mem::size_of::<Weak<Handle<dyn Collect + 'gc>>>()
178}
179
180impl<'gc> Inner<'gc> {
181 fn adopt_handle<T: Collect + 'gc>(this: &Write<Self>, handle: &Rc<Handle<T>>) {
182 let root_size = mem::size_of::<T>();
186 this.metrics.mark_external_allocation(root_size);
187
188 let handles = unlock!(this, Inner, handles);
189 let mut handles = handles.borrow_mut();
190 let old_size = size_of_handle_list(&handles);
191 handles.push(HandleStore {
192 handle: Rc::<Handle<T>>::downgrade(handle),
193 root_size,
194 });
195 let new_size = size_of_handle_list(&handles);
196
197 if new_size > old_size {
198 this.metrics.mark_external_allocation(new_size - old_size);
199 } else if old_size > new_size {
200 this.metrics.mark_external_deallocation(old_size - new_size);
201 }
202 }
203}
204
205impl<'gc> Drop for Inner<'gc> {
206 fn drop(&mut self) {
207 let handles = self.handles.borrow();
208 self.metrics
209 .mark_external_deallocation(handles.iter().map(|s| s.root_size).sum());
210 self.metrics
211 .mark_external_deallocation(size_of_handle_list(&self.handles.borrow()));
212 }
213}
214
215unsafe impl<'gc> Collect for Inner<'gc> {
216 fn trace(&self, cc: &crate::Collection) {
217 let handles = unsafe { self.handles.as_ref_cell() };
222 handles.borrow_mut().retain(|store| {
223 if let Some(handle) = store.handle.upgrade() {
224 handle.root.trace(cc);
225 true
226 } else {
227 cc.metrics().mark_external_deallocation(store.root_size);
228 false
229 }
230 });
231 }
232}
233
234struct Handle<T: ?Sized> {
235 set_id: Rc<SetId>,
238 root: T,
239}