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        #[allow(unsafe_code)]
19        unsafe impl<F> crate::JSTraceable for $name<F> {
20            #[allow(unsafe_code)]
21            unsafe fn trace(&self, tracer: *mut ::js::jsapi::JSTracer) {
22                $(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) {
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) {
54                (self.0)();
55            }
56        }
57        $name(move || $body)
58    }};
59}
60
61/// A task that can be sent between threads and run.
62/// The name method is for profiling purposes.
63pub(crate) trait TaskOnce: Send {
64    fn name(&self) -> &'static str {
65        ::std::any::type_name::<Self>()
66    }
67
68    fn run_once(self);
69}
70
71/// A task that must be run on the same thread it originated in.
72pub(crate) trait NonSendTaskOnce: crate::JSTraceable {
73    fn run_once(self);
74}
75
76/// A boxed version of `TaskOnce`.
77pub(crate) trait TaskBox: Send {
78    fn name(&self) -> &'static str;
79
80    fn run_box(self: Box<Self>);
81}
82
83/// A boxed version of `NonSendTaskOnce`.
84pub(crate) trait NonSendTaskBox: crate::JSTraceable {
85    fn run_box(self: Box<Self>);
86}
87
88impl<T> NonSendTaskBox for T
89where
90    T: NonSendTaskOnce,
91{
92    fn run_box(self: Box<Self>) {
93        self.run_once()
94    }
95}
96
97impl<T> TaskBox for T
98where
99    T: TaskOnce,
100{
101    fn name(&self) -> &'static str {
102        TaskOnce::name(self)
103    }
104
105    fn run_box(self: Box<Self>) {
106        self.run_once()
107    }
108}
109
110impl fmt::Debug for dyn TaskBox {
111    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
112        fmt.debug_tuple(self.name())
113            .field(&format_args!("..."))
114            .finish()
115    }
116}
117
118/// Encapsulated state required to create cancellable tasks from non-script threads.
119#[derive(Clone, Default, JSTraceable, MallocSizeOf)]
120pub(crate) struct TaskCanceller {
121    #[conditional_malloc_size_of]
122    pub(crate) cancelled: Arc<AtomicBool>,
123}
124
125impl TaskCanceller {
126    /// Returns a wrapped `task` that will be cancelled if the `TaskCanceller` says so.
127    pub(crate) fn wrap_task<T>(&self, task: T) -> impl TaskOnce + use<T>
128    where
129        T: TaskOnce,
130    {
131        CancellableTask {
132            canceller: self.clone(),
133            inner: task,
134        }
135    }
136
137    pub(crate) fn cancelled(&self) -> bool {
138        self.cancelled.load(Ordering::SeqCst)
139    }
140}
141
142/// A task that can be cancelled by toggling a shared flag.
143pub(crate) struct CancellableTask<T: TaskOnce> {
144    canceller: TaskCanceller,
145    inner: T,
146}
147
148impl<T: TaskOnce> TaskOnce for CancellableTask<T> {
149    fn name(&self) -> &'static str {
150        self.inner.name()
151    }
152
153    fn run_once(self) {
154        if !self.canceller.cancelled() {
155            self.inner.run_once()
156        }
157    }
158}