script/
task.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */
4
5//! Machinery for [tasks](https://html.spec.whatwg.org/multipage/#concept-task).
6
7use 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                // We cannot trace the actual task closure. This is safe because
24                // all referenced values from within the closure are either borrowed
25                // or moved into fields in the struct (and therefore traced).
26            }
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                // We cannot trace the actual task closure. This is safe because
72                // all referenced values from within the closure are either borrowed
73                // or moved into fields in the struct (and therefore traced).
74            }
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
109/// A task that can be sent between threads and run.
110/// The name method is for profiling purposes.
111pub(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
119/// A task that must be run on the same thread it originated in.
120pub(crate) trait NonSendTaskOnce: crate::JSTraceable {
121    fn run_once(self, cx: &mut js::context::JSContext);
122}
123
124/// A boxed version of `TaskOnce`.
125pub(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
131/// A boxed version of `NonSendTaskOnce`.
132pub(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/// Encapsulated state required to create cancellable tasks from non-script threads.
167#[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    /// Returns a wrapped `task` that will be cancelled if the `TaskCanceller` says so.
175    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
190/// A task that can be cancelled by toggling a shared flag.
191pub(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}