Skip to main content

wasm_actor_bridge/
cancel.rs

1//! Lightweight cancellation token for WASM actors.
2//!
3//! Dual implementation:
4//! - **WASM**: `Rc<Cell<>>` — single-threaded, `!Send`.
5//! - **Native**: `Arc<AtomicBool>` — `Send + Sync` for test convenience.
6
7// ── Native implementation (for testing) ──────────────────────
8
9#[cfg(not(target_arch = "wasm32"))]
10mod native_impl {
11    use std::future::Future;
12    use std::pin::Pin;
13    use std::sync::Arc;
14    use std::sync::atomic::{AtomicBool, Ordering};
15    use std::task::{Context, Poll};
16
17    /// A cancellation token that can be checked synchronously or awaited.
18    ///
19    /// `Clone + Send + Sync` on native for test ergonomics.
20    #[derive(Clone)]
21    pub struct CancellationToken {
22        cancelled: Arc<AtomicBool>,
23    }
24
25    /// Dropping the guard cancels the associated token.
26    pub struct CancelGuard {
27        cancelled: Arc<AtomicBool>,
28    }
29
30    impl CancellationToken {
31        /// Create a new token/guard pair.
32        ///
33        /// The `CancelGuard` cancels the token when dropped.
34        pub fn new() -> (Self, CancelGuard) {
35            let cancelled = Arc::new(AtomicBool::new(false));
36            (
37                Self {
38                    cancelled: Arc::clone(&cancelled),
39                },
40                CancelGuard { cancelled },
41            )
42        }
43
44        /// Check if cancellation has been requested.
45        pub fn is_cancelled(&self) -> bool {
46            self.cancelled.load(Ordering::Acquire)
47        }
48
49        /// Returns a future that resolves when cancellation is requested.
50        pub fn cancelled(&self) -> CancelledFuture {
51            CancelledFuture {
52                token: self.clone(),
53            }
54        }
55    }
56
57    impl Drop for CancelGuard {
58        fn drop(&mut self) {
59            self.cancelled.store(true, Ordering::Release);
60        }
61    }
62
63    /// Future that resolves when the token is cancelled.
64    pub struct CancelledFuture {
65        token: CancellationToken,
66    }
67
68    impl Future for CancelledFuture {
69        type Output = ();
70
71        fn poll(self: Pin<&mut Self>, _cx: &mut Context<'_>) -> Poll<()> {
72            if self.token.is_cancelled() {
73                Poll::Ready(())
74            } else {
75                Poll::Pending
76            }
77        }
78    }
79}
80
81#[cfg(not(target_arch = "wasm32"))]
82pub use native_impl::*;
83
84// ── WASM implementation ──────────────────────────────────────
85
86#[cfg(target_arch = "wasm32")]
87mod wasm_impl {
88    use std::cell::Cell;
89    use std::future::Future;
90    use std::pin::Pin;
91    use std::rc::Rc;
92    use std::task::{Context, Poll, Waker};
93
94    /// A cancellation token that can be checked synchronously or awaited.
95    ///
96    /// `Clone + !Send` — designed for the single-threaded WASM event loop.
97    #[derive(Clone)]
98    pub struct CancellationToken {
99        inner: Rc<TokenInner>,
100    }
101
102    struct TokenInner {
103        cancelled: Cell<bool>,
104        waker: Cell<Option<Waker>>,
105    }
106
107    /// Dropping the guard cancels the associated token.
108    pub struct CancelGuard {
109        inner: Rc<TokenInner>,
110    }
111
112    impl CancellationToken {
113        /// Create a new token/guard pair.
114        ///
115        /// The `CancelGuard` cancels the token when dropped.
116        pub fn new() -> (Self, CancelGuard) {
117            let inner = Rc::new(TokenInner {
118                cancelled: Cell::new(false),
119                waker: Cell::new(None),
120            });
121            let token = Self {
122                inner: Rc::clone(&inner),
123            };
124            let guard = CancelGuard { inner };
125            (token, guard)
126        }
127
128        /// Check if cancellation has been requested.
129        pub fn is_cancelled(&self) -> bool {
130            self.inner.cancelled.get()
131        }
132
133        /// Returns a future that resolves when cancellation is requested.
134        pub fn cancelled(&self) -> CancelledFuture {
135            CancelledFuture {
136                token: self.clone(),
137            }
138        }
139    }
140
141    impl Drop for CancelGuard {
142        fn drop(&mut self) {
143            self.inner.cancelled.set(true);
144            if let Some(waker) = self.inner.waker.take() {
145                waker.wake();
146            }
147        }
148    }
149
150    /// Future that resolves when the token is cancelled.
151    pub struct CancelledFuture {
152        token: CancellationToken,
153    }
154
155    impl Future for CancelledFuture {
156        type Output = ();
157
158        fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
159            if self.token.is_cancelled() {
160                Poll::Ready(())
161            } else {
162                self.token.inner.waker.set(Some(cx.waker().clone()));
163                Poll::Pending
164            }
165        }
166    }
167}
168
169#[cfg(target_arch = "wasm32")]
170pub use wasm_impl::*;