script/dom/bindings/
refcounted.rs1use std::cell::RefCell;
10use std::sync::{Arc, Weak};
11use std::thread::{self, ThreadId};
12
13use js::context::JSContext;
14use js::conversions::ToJSValConvertible;
15use js::jsapi::JSTracer;
16use js::rust::Trace;
17use rustc_hash::FxHashMap;
18use script_bindings::error::Error;
19use script_bindings::interfaces::ThreadTrustedPromiseHelpers;
20pub(crate) use script_bindings::refcounted::Trusted;
21use script_bindings::reflector::DomObject;
22
23use crate::dom::promise::{Promise, RootedPromise, TracedPromise};
24use crate::tasks::task::TaskOnce;
25
26thread_local!(pub(super) static LIVE_PROMISE_REFERENCES: LivePromiseReferences =
27 LivePromiseReferences {
28 promise_table: RefCell::new(FxHashMap::default()),
29 }
30);
31
32#[derive(Clone, Copy, Eq, Hash, PartialEq)]
33struct PromiseKey(*const Promise);
34
35unsafe impl Send for PromiseKey {}
39unsafe impl Sync for PromiseKey {}
40
41#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
44pub(crate) struct LivePromiseReferences {
45 promise_table: RefCell<FxHashMap<PromiseKey, (TracedPromise, Weak<PromiseKey>)>>,
48}
49
50impl LivePromiseReferences {
51 pub(crate) fn expensive_prune_dropped_promises() {
54 LIVE_PROMISE_REFERENCES.with(|live_promise_references| {
55 live_promise_references
56 .promise_table
57 .borrow_mut()
58 .retain(|_, (_, key)| key.strong_count() > 0);
59 })
60 }
61
62 pub(crate) fn destruct() {
63 LIVE_PROMISE_REFERENCES.with(|live_references| {
64 let _ = live_references.promise_table.take();
65 });
66 }
67
68 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
69 fn store(&self, promise: &RootedPromise) -> TrustedPromise {
70 let traced_promise = promise.to_traced();
74
75 let mut promise_table = self.promise_table.borrow_mut();
76
77 if promise_table.len() == promise_table.capacity() {
81 promise_table.retain(|_, (_, key)| key.strong_count() > 0);
82 }
83
84 let key = Arc::new(PromiseKey(&raw const *traced_promise));
85 let _exists = promise_table
86 .insert(*key, (traced_promise, Arc::downgrade(&key)))
87 .is_some();
88 debug_assert!(!_exists);
89
90 TrustedPromise {
91 key,
92 owner_thread: thread::current().id(),
93 }
94 }
95
96 fn remove(&self, key: &PromiseKey) -> Option<TracedPromise> {
97 self.promise_table
98 .borrow_mut()
99 .remove(key)
100 .map(|(promise, _)| promise)
101 }
102}
103
104pub(crate) struct TrustedPromise {
109 key: Arc<PromiseKey>,
110 owner_thread: ThreadId,
111}
112
113impl TrustedPromise {
114 pub(crate) fn new(promise: &RootedPromise) -> TrustedPromise {
118 LIVE_PROMISE_REFERENCES
119 .with(|live_promise_references| live_promise_references.store(promise))
120 }
121
122 pub(crate) fn root(self, cx: &JSContext) -> RootedPromise {
126 LIVE_PROMISE_REFERENCES.with(|live_references| {
127 debug_assert_eq!(self.owner_thread, thread::current().id());
128 live_references
129 .promise_table
130 .borrow()
131 .get(&self.key)
132 .expect("Must always have an entry matching a TrustedPromise")
133 .0
134 .root(cx)
135 })
136 }
137
138 pub(crate) fn reject_task(self, error: Error) -> impl TaskOnce {
140 let this = self;
141 task!(reject_promise: move |cx| {
142 debug!("Rejecting promise.");
143 this.root(cx).reject_error(cx, error);
144 })
145 }
146
147 pub(crate) fn resolve_task<T>(self, value: T) -> impl TaskOnce
149 where
150 T: ToJSValConvertible + Send,
151 {
152 let this = self;
153 task!(resolve_promise: move |cx| {
154 debug!("Resolving promise.");
155 this.root(cx).resolve_native(cx, &value);
156 })
157 }
158}
159
160impl Drop for TrustedPromise {
161 fn drop(&mut self) {
162 if self.owner_thread != thread::current().id() {
164 return;
165 }
166 let _ = LIVE_PROMISE_REFERENCES
168 .try_with(|live_promise_references| live_promise_references.remove(&self.key));
169 }
170}
171
172impl ThreadTrustedPromiseHelpers<crate::DomTypeHolder> for TrustedPromise {
173 type StackRoot = RootedPromise;
174
175 fn root(self, cx: &JSContext) -> RootedPromise {
176 TrustedPromise::root(self, cx)
177 }
178}
179
180pub(crate) unsafe fn trace_refcounted_objects(tracer: *mut JSTracer) {
182 trace!("tracing live refcounted promise references");
183 LIVE_PROMISE_REFERENCES.with(|live_references| {
184 let table = live_references.promise_table.borrow();
185 for (promise, _) in table.values() {
186 unsafe {
187 promise.reflector().trace(tracer);
188 }
189 }
190 });
191 trace!("tracing live refcounted references");
192 unsafe {
193 script_bindings::refcounted::trace_live_domreferences(tracer);
194 }
195}