Skip to main content

workflow_core/
abortable.rs

1//!
2//! Abortable trigger, can be used to cancel (abort) an asynchronous task.
3//!
4
5use wasm_bindgen::prelude::*;
6
7use std::sync::{
8    Arc,
9    atomic::{AtomicBool, Ordering},
10};
11
12/// Error emitted by [`Abortable`].
13/// @category General
14#[wasm_bindgen]
15pub struct Aborted;
16
17impl std::error::Error for Aborted {}
18
19impl std::fmt::Debug for Aborted {
20    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21        write!(f, "task aborted")
22    }
23}
24
25impl std::fmt::Display for Aborted {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        write!(f, "task aborted")
28    }
29}
30
31///
32/// Abortable trigger wraps an `Arc<AtomicBool>`, which can be cloned
33/// to signal task terminating using an atomic bool.
34///
35/// ```text
36/// let abortable = Abortable::default();
37/// let result = my_task(abortable).await?;
38/// // ... elsewhere
39/// abortable.abort();
40/// ```
41///
42/// @category General
43#[derive(Default, Clone)]
44#[wasm_bindgen]
45pub struct Abortable(Arc<AtomicBool>);
46
47#[wasm_bindgen]
48impl Abortable {
49    /// Creates a new [`Abortable`] in the non-aborted state.
50    #[wasm_bindgen(constructor)]
51    pub fn new() -> Self {
52        Self(Arc::new(AtomicBool::new(false)))
53    }
54
55    /// Returns `true` if [`abort`](Self::abort) has been signalled.
56    #[wasm_bindgen(js_name=isAborted)]
57    pub fn is_aborted(&self) -> bool {
58        self.0.load(Ordering::SeqCst)
59    }
60
61    /// Signals abort, causing subsequent [`check`](Self::check) calls to fail.
62    pub fn abort(&self) {
63        self.0.store(true, Ordering::SeqCst);
64    }
65
66    /// Returns `Err(`[`Aborted`]`)` if abort has been signalled, otherwise `Ok(())`.
67    pub fn check(&self) -> Result<(), Aborted> {
68        if self.is_aborted() {
69            Err(Aborted)
70        } else {
71            Ok(())
72        }
73    }
74
75    /// Clears the abort signal, returning the trigger to its non-aborted state.
76    pub fn reset(&self) {
77        self.0.store(false, Ordering::SeqCst);
78    }
79}
80
81impl TryFrom<&JsValue> for Abortable {
82    type Error = JsValue;
83    fn try_from(value: &JsValue) -> Result<Self, Self::Error> {
84        use wasm_bindgen::convert::*;
85
86        let idx = IntoWasmAbi::into_abi(value);
87        #[link(wasm_import_module = "__wbindgen_placeholder__")]
88        #[cfg(all(
89            target_arch = "wasm32",
90            not(any(target_os = "emscripten", target_os = "wasi"))
91        ))]
92        unsafe extern "C" {
93            fn __wbg_abortable_unwrap(ptr: u32) -> u32;
94        }
95        #[cfg(not(all(
96            target_arch = "wasm32",
97            not(any(target_os = "emscripten", target_os = "wasi"))
98        )))]
99        unsafe fn __wbg_abortable_unwrap(_: u32) -> u32 {
100            panic!("cannot convert from JsValue outside of the wasm target")
101        }
102        let ptr = unsafe { __wbg_abortable_unwrap(idx) };
103        if ptr == 0 {
104            wasm_bindgen::__rt::std::result::Result::Err(value.clone())
105        } else {
106            unsafe {
107                // wasm-bindgen 0.2.126 changed the exported-struct ABI from a
108                // bare u32 to `WasmPtr`; wrap the unwrapped pointer accordingly.
109                wasm_bindgen::__rt::std::result::Result::Ok(
110                    <Self as FromWasmAbi>::from_abi(wasm_bindgen::__rt::WasmPtr::from_usize(
111                        ptr as usize,
112                    ))
113                    .clone(),
114                )
115            }
116        }
117    }
118}