/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ //! Machinery for [tasks](https://html.spec.whatwg.org/multipage/#concept-task). use std::fmt; use std::sync::atomic::{AtomicBool, Ordering}; use std::sync::Arc; macro_rules! task { ($name:ident: move || $body:tt) => {{ #[allow(non_camel_case_types)] struct $name(F); impl crate::task::TaskOnce for $name where F: ::std::ops::FnOnce() + Send, { fn name(&self) -> &'static str { stringify!($name) } fn run_once(self) { (self.0)(); } } $name(move || $body) }}; } /// A task that can be run. The name method is for profiling purposes. pub trait TaskOnce: Send { #[allow(unsafe_code)] fn name(&self) -> &'static str { ::std::any::type_name::() } fn run_once(self); } /// A boxed version of `TaskOnce`. pub trait TaskBox: Send { fn name(&self) -> &'static str; fn run_box(self: Box); } impl TaskBox for T where T: TaskOnce, { fn name(&self) -> &'static str { TaskOnce::name(self) } fn run_box(self: Box) { self.run_once() } } impl fmt::Debug for dyn TaskBox { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { fmt.debug_tuple(self.name()) .field(&format_args!("...")) .finish() } } /// Encapsulated state required to create cancellable tasks from non-script threads. #[derive(Clone)] pub struct TaskCanceller { pub cancelled: Arc, } impl TaskCanceller { /// Returns a wrapped `task` that will be cancelled if the `TaskCanceller` /// says so. pub fn wrap_task(&self, task: T) -> impl TaskOnce where T: TaskOnce, { CancellableTask { cancelled: self.cancelled.clone(), inner: task, } } } /// A task that can be cancelled by toggling a shared flag. pub struct CancellableTask { cancelled: Arc, inner: T, } impl CancellableTask where T: TaskOnce, { fn is_cancelled(&self) -> bool { self.cancelled.load(Ordering::SeqCst) } } impl TaskOnce for CancellableTask where T: TaskOnce, { fn name(&self) -> &'static str { self.inner.name() } fn run_once(self) { if !self.is_cancelled() { self.inner.run_once() } } }