1use std::fmt;
8use std::sync::Arc;
9use std::sync::atomic::{AtomicBool, Ordering};
10
11macro_rules! task {
12 ($name:ident: |$($field:ident: $field_type:ty$(,)*)*| $body:tt) => {{
13 #[allow(non_camel_case_types)]
14 struct $name<F> {
15 $($field: $field_type,)*
16 task: F,
17 }
18 #[expect(unsafe_code)]
19 unsafe impl<F> crate::JSTraceable for $name<F> {
20 #[expect(unsafe_code)]
21 unsafe fn trace(&self, tracer: *mut ::js::jsapi::JSTracer) {
22 unsafe { $(self.$field.trace(tracer);)* }
23 }
27 }
28 impl<F> crate::task::NonSendTaskOnce for $name<F>
29 where
30 F: ::std::ops::FnOnce($($field_type,)*),
31 {
32 fn run_once(self, _cx: &mut js::context::JSContext) {
33 (self.task)($(self.$field,)*);
34 }
35 }
36 $name {
37 $($field,)*
38 task: |$($field: $field_type,)*| $body,
39 }
40 }};
41
42 ($name:ident: move || $body:tt) => {{
43 #[allow(non_camel_case_types)]
44 struct $name<F>(F);
45 impl<F> crate::task::TaskOnce for $name<F>
46 where
47 F: ::std::ops::FnOnce() + Send,
48 {
49 fn name(&self) -> &'static str {
50 stringify!($name)
51 }
52
53 fn run_once(self, _cx: &mut js::context::JSContext) {
54 (self.0)();
55 }
56 }
57 $name(move || $body)
58 }};
59
60 ($name:ident: |$cx: ident $(, $field:ident: $field_type:ty)*| $body:tt) => {{
61 #[allow(non_camel_case_types)]
62 struct $name<F> {
63 $($field: $field_type,)*
64 task: F,
65 }
66 #[expect(unsafe_code)]
67 unsafe impl<F> crate::JSTraceable for $name<F> {
68 #[expect(unsafe_code)]
69 unsafe fn trace(&self, tracer: *mut ::js::jsapi::JSTracer) {
70 unsafe { $(self.$field.trace(tracer);)* }
71 }
75 }
76 impl<F> crate::task::NonSendTaskOnce for $name<F>
77 where
78 F: ::std::ops::FnOnce(&mut js::context::JSContext, $($field_type,)*),
79 {
80 fn run_once(self, cx: &mut js::context::JSContext) {
81 (self.task)(cx, $(self.$field,)*);
82 }
83 }
84 $name {
85 $($field,)*
86 task: |$cx: &mut js::context::JSContext, $($field: $field_type,)*| $body,
87 }
88 }};
89
90 ($name:ident: move |$cx: ident| $body:tt) => {{
91 #[allow(non_camel_case_types)]
92 struct $name<F>(F);
93 impl<F> crate::task::TaskOnce for $name<F>
94 where
95 F: ::std::ops::FnOnce(&mut js::context::JSContext) + Send,
96 {
97 fn name(&self) -> &'static str {
98 stringify!($name)
99 }
100
101 fn run_once(self, cx: &mut js::context::JSContext) {
102 (self.0)(cx);
103 }
104 }
105 $name(move |$cx: &mut js::context::JSContext| $body)
106 }};
107}
108
109pub(crate) trait TaskOnce: Send {
112 fn name(&self) -> &'static str {
113 ::std::any::type_name::<Self>()
114 }
115
116 fn run_once(self, cx: &mut js::context::JSContext);
117}
118
119pub(crate) trait NonSendTaskOnce: crate::JSTraceable {
121 fn run_once(self, cx: &mut js::context::JSContext);
122}
123
124pub(crate) trait TaskBox: Send {
126 fn name(&self) -> &'static str;
127
128 fn run_box(self: Box<Self>, cx: &mut js::context::JSContext);
129}
130
131pub(crate) trait NonSendTaskBox: crate::JSTraceable {
133 fn run_box(self: Box<Self>, cx: &mut js::context::JSContext);
134}
135
136impl<T> NonSendTaskBox for T
137where
138 T: NonSendTaskOnce,
139{
140 fn run_box(self: Box<Self>, cx: &mut js::context::JSContext) {
141 self.run_once(cx)
142 }
143}
144
145impl<T> TaskBox for T
146where
147 T: TaskOnce,
148{
149 fn name(&self) -> &'static str {
150 TaskOnce::name(self)
151 }
152
153 fn run_box(self: Box<Self>, cx: &mut js::context::JSContext) {
154 self.run_once(cx)
155 }
156}
157
158impl fmt::Debug for dyn TaskBox {
159 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
160 fmt.debug_tuple(self.name())
161 .field(&format_args!("..."))
162 .finish()
163 }
164}
165
166#[derive(Clone, Default, JSTraceable, MallocSizeOf)]
168pub(crate) struct TaskCanceller {
169 #[conditional_malloc_size_of]
170 pub(crate) cancelled: Arc<AtomicBool>,
171}
172
173impl TaskCanceller {
174 pub(crate) fn wrap_task<T>(&self, task: T) -> impl TaskOnce + use<T>
176 where
177 T: TaskOnce,
178 {
179 CancellableTask {
180 canceller: self.clone(),
181 inner: task,
182 }
183 }
184
185 pub(crate) fn cancelled(&self) -> bool {
186 self.cancelled.load(Ordering::SeqCst)
187 }
188}
189
190pub(crate) struct CancellableTask<T: TaskOnce> {
192 canceller: TaskCanceller,
193 inner: T,
194}
195
196impl<T: TaskOnce> TaskOnce for CancellableTask<T> {
197 fn name(&self) -> &'static str {
198 self.inner.name()
199 }
200
201 fn run_once(self, cx: &mut js::context::JSContext) {
202 if !self.canceller.cancelled() {
203 self.inner.run_once(cx)
204 }
205 }
206}