script/dom/bindings/
refcounted.rs1use std::cell::RefCell;
10use std::thread::{self, ThreadId};
11
12use js::context::JSContext;
13use js::conversions::ToJSValConvertible;
14use js::jsapi::JSTracer;
15use js::rust::Trace;
16use rustc_hash::FxHashMap;
17use script_bindings::error::Error;
18use script_bindings::interfaces::ThreadTrustedPromiseHelpers;
19pub(crate) use script_bindings::refcounted::Trusted;
20use script_bindings::reflector::DomObject;
21
22use crate::dom::promise::{Promise, RootedPromise, TracedPromise};
23use crate::tasks::task::TaskOnce;
24
25thread_local!(pub(super) static LIVE_PROMISE_REFERENCES: LivePromiseReferences =
26 LivePromiseReferences {
27 promise_table: RefCell::new(FxHashMap::default()),
28 }
29);
30
31#[derive(Clone, Copy, Eq, Hash, PartialEq)]
32struct PromiseKey(*const Promise);
33
34unsafe impl Send for PromiseKey {}
38
39#[cfg_attr(crown, crown::unrooted_must_root_lint::must_root)]
42pub(crate) struct LivePromiseReferences {
43 promise_table: RefCell<FxHashMap<PromiseKey, TracedPromise>>,
45}
46
47impl LivePromiseReferences {
48 pub(crate) fn destruct() {
49 LIVE_PROMISE_REFERENCES.with(|live_references| {
50 let _ = live_references.promise_table.take();
51 });
52 }
53
54 #[cfg_attr(crown, expect(crown::unrooted_must_root))]
55 fn store_promise(&self, promise: &RootedPromise) -> PromiseKey {
56 let traced_promise = promise.to_traced();
60 let key = PromiseKey(&raw const *traced_promise);
61 let _exists = self
62 .promise_table
63 .borrow_mut()
64 .insert(key, traced_promise)
65 .is_some();
66 debug_assert!(!_exists);
67 key
68 }
69}
70
71pub(crate) struct TrustedPromise {
76 dom_object: PromiseKey,
77 owner_thread: ThreadId,
78}
79
80impl TrustedPromise {
81 pub(crate) fn new(promise: &RootedPromise) -> TrustedPromise {
85 LIVE_PROMISE_REFERENCES.with(|live_references| {
86 let ptr = live_references.store_promise(promise);
87 TrustedPromise {
88 dom_object: ptr,
89 owner_thread: thread::current().id(),
90 }
91 })
92 }
93
94 pub(crate) fn root(self, cx: &JSContext) -> RootedPromise {
98 LIVE_PROMISE_REFERENCES.with(|live_references| {
99 debug_assert_eq!(self.owner_thread, thread::current().id());
100 live_references
101 .promise_table
102 .borrow_mut()
103 .remove(&self.dom_object)
104 .expect("Must always have an entry matching a TrustedPromise")
105 .root(cx)
106 })
107 }
108
109 pub(crate) fn reject_task(self, error: Error) -> impl TaskOnce {
111 let this = self;
112 task!(reject_promise: move |cx| {
113 debug!("Rejecting promise.");
114 this.root(cx).reject_error(cx, error);
115 })
116 }
117
118 pub(crate) fn resolve_task<T>(self, value: T) -> impl TaskOnce
120 where
121 T: ToJSValConvertible + Send,
122 {
123 let this = self;
124 task!(resolve_promise: move |cx| {
125 debug!("Resolving promise.");
126 this.root(cx).resolve_native(cx, &value);
127 })
128 }
129}
130
131impl ThreadTrustedPromiseHelpers<crate::DomTypeHolder> for TrustedPromise {
132 type StackRoot = RootedPromise;
133
134 fn root(self, cx: &JSContext) -> RootedPromise {
135 TrustedPromise::root(self, cx)
136 }
137}
138
139pub(crate) unsafe fn trace_refcounted_objects(tracer: *mut JSTracer) {
141 trace!("tracing live refcounted promise references");
142 LIVE_PROMISE_REFERENCES.with(|live_references| {
143 let table = live_references.promise_table.borrow_mut();
144 for promise in table.values() {
145 unsafe {
146 promise.reflector().trace(tracer);
147 }
148 }
149 });
150 trace!("tracing live refcounted references");
151 unsafe {
152 script_bindings::refcounted::trace_live_domreferences(tracer);
153 }
154}