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
179impl<T: DomObject> std::fmt::Debug for Trusted<T> {
180 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
181 f.write_str("...")
182 }
183}
184
185unsafe impl<T: DomObject> Send for Trusted<T> {}
186
187impl<T: DomObject> Trusted<T> {
188 pub(crate) fn new(ptr: &T) -> Trusted<T> {
192 fn add_live_reference(
193 ptr: *const libc::c_void,
194 ) -> (Arc<TrustedReference>, *const LiveDOMReferences) {
195 LIVE_REFERENCES.with(|r| {
196 let live_references = &*r.borrow();
197 let refcount = unsafe { live_references.addref(ptr) };
198 (refcount, live_references as *const _)
199 })
200 }
201
202 let (refcount, owner_thread) = add_live_reference(ptr as *const T as *const _);
203 Trusted {
204 refcount,
205 owner_thread,
206 phantom: PhantomData,
207 }
208 }
209
210 pub(crate) fn root(&self) -> DomRoot<T> {
214 fn validate(owner_thread: *const LiveDOMReferences) {
215 assert!(LIVE_REFERENCES.with(|r| {
216 let r = r.borrow();
217 let live_references = &*r;
218 owner_thread == live_references
219 }));
220 }
221 validate(self.owner_thread);
222 unsafe { DomRoot::from_ref(&*(self.refcount.0 as *const T)) }
223 }
224}
225
226impl<T: DomObject> Clone for Trusted<T> {
227 fn clone(&self) -> Trusted<T> {
228 Trusted {
229 refcount: self.refcount.clone(),
230 owner_thread: self.owner_thread,
231 phantom: PhantomData,
232 }
233 }
234}
235
236#[cfg_attr(crown, allow(crown::unrooted_must_root))]
239pub(crate) struct LiveDOMReferences {
240 reflectable_table: RefCell<FxHashMap<*const libc::c_void, Weak<TrustedReference>>>,
242 promise_table: RefCell<FxHashMap<*const Promise, Vec<Rc<Promise>>>>,
243}
244
245impl LiveDOMReferences {
246 pub(crate) fn destruct() {
247 LIVE_REFERENCES.with(|r| {
248 let live_references = r.borrow_mut();
249 let _ = live_references.promise_table.take();
250 let _ = live_references.reflectable_table.take();
251 });
252 }
253
254 #[cfg_attr(crown, allow(crown::unrooted_must_root))]
255 fn addref_promise(&self, promise: Rc<Promise>) {
256 let mut table = self.promise_table.borrow_mut();
257 table.entry(&*promise).or_default().push(promise)
258 }
259
260 #[allow(clippy::arc_with_non_send_sync)]
264 unsafe fn addref(&self, ptr: *const libc::c_void) -> Arc<TrustedReference> {
265 let mut table = self.reflectable_table.borrow_mut();
266 let capacity = table.capacity();
267 let len = table.len();
268 if (0 < capacity) && (capacity <= len) {
269 trace!("growing refcounted references by {}", len);
270 remove_nulls(&mut table);
271 table.reserve(len);
272 }
273 match table.entry(ptr) {
274 Occupied(mut entry) => match entry.get().upgrade() {
275 Some(refcount) => refcount,
276 None => {
277 let refcount = Arc::new(unsafe { TrustedReference::new(ptr) });
278 entry.insert(Arc::downgrade(&refcount));
279 refcount
280 },
281 },
282 Vacant(entry) => {
283 let refcount = Arc::new(unsafe { TrustedReference::new(ptr) });
284 entry.insert(Arc::downgrade(&refcount));
285 refcount
286 },
287 }
288 }
289}
290
291fn remove_nulls<K: Eq + Hash + Clone, V>(table: &mut FxHashMap<K, Weak<V>>) {
293 let to_remove: Vec<K> = table
294 .iter()
295 .filter(|&(_, value)| Weak::upgrade(value).is_none())
296 .map(|(key, _)| key.clone())
297 .collect();
298 trace!("removing {} refcounted references", to_remove.len());
299 for key in to_remove {
300 table.remove(&key);
301 }
302}
303
304#[cfg_attr(crown, allow(crown::unrooted_must_root))]
306pub(crate) unsafe fn trace_refcounted_objects(tracer: *mut JSTracer) {
307 trace!("tracing live refcounted references");
308 LIVE_REFERENCES.with(|r| {
309 let live_references = &*r.borrow();
310 {
311 let mut table = live_references.reflectable_table.borrow_mut();
312 remove_nulls(&mut table);
313 for obj in table.keys() {
314 unsafe {
315 trace_reflector(tracer, "refcounted", &*(*obj as *const Reflector));
316 }
317 }
318 }
319
320 {
321 let table = live_references.promise_table.borrow_mut();
322 for promise in table.keys() {
323 unsafe {
324 trace_reflector(tracer, "refcounted", (**promise).reflector());
325 }
326 }
327 }
328 });
329}