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