script/dom/bindings/
refcounted.rs1use std::cell::RefCell;
26use std::collections::hash_map::Entry::{Occupied, Vacant};
27use std::hash::Hash;
28use std::marker::PhantomData;
29use std::rc::Rc;
30use std::sync::{Arc, Weak};
31
32use js::jsapi::JSTracer;
33use rustc_hash::FxHashMap;
34use script_bindings::script_runtime::CanGc;
35
36use crate::dom::bindings::conversions::ToJSValConvertible;
37use crate::dom::bindings::error::Error;
38use crate::dom::bindings::reflector::{DomObject, Reflector};
39use crate::dom::bindings::root::DomRoot;
40use crate::dom::bindings::trace::trace_reflector;
41use crate::dom::promise::Promise;
42use crate::task::TaskOnce;
43
44#[allow(missing_docs)] mod dummy {
46 use std::cell::RefCell;
48 use std::rc::Rc;
49
50 use rustc_hash::FxHashMap;
51
52 use super::LiveDOMReferences;
53 thread_local!(pub(crate) static LIVE_REFERENCES: Rc<RefCell<LiveDOMReferences>> =
54 Rc::new(RefCell::new(
55 LiveDOMReferences {
56 reflectable_table: RefCell::new(FxHashMap::default()),
57 promise_table: RefCell::new(FxHashMap::default()),
58 }
59 )));
60}
61pub(crate) use self::dummy::LIVE_REFERENCES;
62
63#[derive(MallocSizeOf)]
65struct TrustedReference(
66 #[ignore_malloc_size_of = "This is a shared reference."] *const libc::c_void,
67);
68unsafe impl Send for TrustedReference {}
69
70impl TrustedReference {
71 unsafe fn new(ptr: *const libc::c_void) -> TrustedReference {
75 TrustedReference(ptr)
76 }
77}
78
79pub struct TrustedPromise {
84 dom_object: *const Promise,
85 owner_thread: *const libc::c_void,
86}
87
88unsafe impl Send for TrustedPromise {}
89
90impl TrustedPromise {
91 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
95 pub(crate) fn new(promise: Rc<Promise>) -> TrustedPromise {
96 LIVE_REFERENCES.with(|r| {
97 let live_references = &*r.borrow();
98 let ptr = &raw const *promise;
99 live_references.addref_promise(promise);
100 TrustedPromise {
101 dom_object: ptr,
102 owner_thread: (live_references) as *const _ as *const libc::c_void,
103 }
104 })
105 }
106
107 pub(crate) fn root(self) -> Rc<Promise> {
111 LIVE_REFERENCES.with(|r| {
112 let live_references = &*r.borrow();
113 assert_eq!(
114 self.owner_thread,
115 live_references as *const _ as *const libc::c_void
116 );
117 match live_references
118 .promise_table
119 .borrow_mut()
120 .entry(self.dom_object)
121 {
122 Occupied(mut entry) => {
123 let promise = {
124 let promises = entry.get_mut();
125 promises
126 .pop()
127 .expect("rooted promise list unexpectedly empty")
128 };
129 if entry.get().is_empty() {
130 entry.remove();
131 }
132 promise
133 },
134 Vacant(_) => unreachable!(),
135 }
136 })
137 }
138
139 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
141 pub(crate) fn reject_task(self, error: Error) -> impl TaskOnce {
142 let this = self;
143 task!(reject_promise: move || {
144 debug!("Rejecting promise.");
145 this.root().reject_error(error, CanGc::note());
146 })
147 }
148
149 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
151 pub(crate) fn resolve_task<T>(self, value: T) -> impl TaskOnce
152 where
153 T: ToJSValConvertible + Send,
154 {
155 let this = self;
156 task!(resolve_promise: move || {
157 debug!("Resolving promise.");
158 this.root().resolve_native(&value, CanGc::note());
159 })
160 }
161}
162
163#[cfg_attr(crown, crown::unrooted_must_root_lint::allow_unrooted_interior)]
168#[derive(MallocSizeOf)]
169pub(crate) struct Trusted<T: DomObject> {
170 #[conditional_malloc_size_of]
173 refcount: Arc<TrustedReference>,
174 #[ignore_malloc_size_of = "These are shared by all `Trusted` types."]
175 owner_thread: *const LiveDOMReferences,
176 phantom: PhantomData<T>,
177}
178
179unsafe impl<T: DomObject> Send for Trusted<T> {}
180
181impl<T: DomObject> Trusted<T> {
182 pub(crate) fn new(ptr: &T) -> Trusted<T> {
186 fn add_live_reference(
187 ptr: *const libc::c_void,
188 ) -> (Arc<TrustedReference>, *const LiveDOMReferences) {
189 LIVE_REFERENCES.with(|r| {
190 let live_references = &*r.borrow();
191 let refcount = unsafe { live_references.addref(ptr) };
192 (refcount, live_references as *const _)
193 })
194 }
195
196 let (refcount, owner_thread) = add_live_reference(ptr as *const T as *const _);
197 Trusted {
198 refcount,
199 owner_thread,
200 phantom: PhantomData,
201 }
202 }
203
204 pub(crate) fn root(&self) -> DomRoot<T> {
208 fn validate(owner_thread: *const LiveDOMReferences) {
209 assert!(LIVE_REFERENCES.with(|r| {
210 let r = r.borrow();
211 let live_references = &*r;
212 owner_thread == live_references
213 }));
214 }
215 validate(self.owner_thread);
216 unsafe { DomRoot::from_ref(&*(self.refcount.0 as *const T)) }
217 }
218}
219
220impl<T: DomObject> Clone for Trusted<T> {
221 fn clone(&self) -> Trusted<T> {
222 Trusted {
223 refcount: self.refcount.clone(),
224 owner_thread: self.owner_thread,
225 phantom: PhantomData,
226 }
227 }
228}
229
230#[cfg_attr(crown, allow(crown::unrooted_must_root))]
233pub(crate) struct LiveDOMReferences {
234 reflectable_table: RefCell<FxHashMap<*const libc::c_void, Weak<TrustedReference>>>,
236 promise_table: RefCell<FxHashMap<*const Promise, Vec<Rc<Promise>>>>,
237}
238
239impl LiveDOMReferences {
240 pub(crate) fn destruct() {
241 LIVE_REFERENCES.with(|r| {
242 let live_references = r.borrow_mut();
243 let _ = live_references.promise_table.take();
244 let _ = live_references.reflectable_table.take();
245 });
246 }
247
248 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
249 fn addref_promise(&self, promise: Rc<Promise>) {
250 let mut table = self.promise_table.borrow_mut();
251 table.entry(&*promise).or_default().push(promise)
252 }
253
254 #[allow(clippy::arc_with_non_send_sync)]
258 unsafe fn addref(&self, ptr: *const libc::c_void) -> Arc<TrustedReference> {
259 let mut table = self.reflectable_table.borrow_mut();
260 let capacity = table.capacity();
261 let len = table.len();
262 if (0 < capacity) && (capacity <= len) {
263 trace!("growing refcounted references by {}", len);
264 remove_nulls(&mut table);
265 table.reserve(len);
266 }
267 match table.entry(ptr) {
268 Occupied(mut entry) => match entry.get().upgrade() {
269 Some(refcount) => refcount,
270 None => {
271 let refcount = Arc::new(unsafe { TrustedReference::new(ptr) });
272 entry.insert(Arc::downgrade(&refcount));
273 refcount
274 },
275 },
276 Vacant(entry) => {
277 let refcount = Arc::new(unsafe { TrustedReference::new(ptr) });
278 entry.insert(Arc::downgrade(&refcount));
279 refcount
280 },
281 }
282 }
283}
284
285fn remove_nulls<K: Eq + Hash + Clone, V>(table: &mut FxHashMap<K, Weak<V>>) {
287 let to_remove: Vec<K> = table
288 .iter()
289 .filter(|&(_, value)| Weak::upgrade(value).is_none())
290 .map(|(key, _)| key.clone())
291 .collect();
292 trace!("removing {} refcounted references", to_remove.len());
293 for key in to_remove {
294 table.remove(&key);
295 }
296}
297
298#[cfg_attr(crown, allow(crown::unrooted_must_root))]
300pub(crate) unsafe fn trace_refcounted_objects(tracer: *mut JSTracer) {
301 trace!("tracing live refcounted references");
302 LIVE_REFERENCES.with(|r| {
303 let live_references = &*r.borrow();
304 {
305 let mut table = live_references.reflectable_table.borrow_mut();
306 remove_nulls(&mut table);
307 for obj in table.keys() {
308 unsafe {
309 trace_reflector(tracer, "refcounted", &*(*obj as *const Reflector));
310 }
311 }
312 }
313
314 {
315 let table = live_references.promise_table.borrow_mut();
316 for promise in table.keys() {
317 unsafe {
318 trace_reflector(tracer, "refcounted", (**promise).reflector());
319 }
320 }
321 }
322 });
323}