1use futures::future::{select, Either};
3use std::{future::Future, pin::pin};
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq)]
6pub struct Aborted;
7impl std::fmt::Display for Aborted {
8 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
9 f.write_str("operation aborted")
10 }
11}
12impl std::error::Error for Aborted {}
13
14#[cfg(not(target_arch = "wasm32"))]
15mod platform {
16 use tokio_util::sync::CancellationToken;
17
18 #[derive(Clone, Debug, Default)]
19 pub struct AbortController(CancellationToken);
20 #[derive(Clone, Debug)]
21 pub struct AbortSignal(CancellationToken);
22
23 impl AbortController {
24 pub fn new() -> Self {
25 Self::default()
26 }
27 pub fn abort(&self) {
28 self.0.cancel();
29 }
30 pub fn signal(&self) -> AbortSignal {
31 AbortSignal(self.0.clone())
32 }
33 pub fn child_of(signal: &AbortSignal) -> Self {
35 Self(signal.0.child_token())
36 }
37 }
38 impl AbortSignal {
39 pub fn aborted(&self) -> bool {
40 self.0.is_cancelled()
41 }
42 pub async fn cancelled(&self) {
43 self.0.cancelled().await
44 }
45 }
46}
47#[cfg(target_arch = "wasm32")]
48mod platform {
49 use futures::channel::oneshot;
50 use wasm_bindgen::{
51 convert::{FromWasmAbi, OptionFromWasmAbi},
52 describe::WasmDescribe,
53 };
54
55 #[derive(Clone, Debug)]
56 pub struct AbortSignal(web_sys::AbortSignal);
57 #[derive(Clone, Debug)]
58 pub struct AbortController(web_sys::AbortController);
59
60 impl AbortController {
61 pub fn new() -> Self {
62 Self(
63 web_sys::AbortController::new()
64 .expect("AbortController is required on this platform"),
65 )
66 }
67 pub fn abort(&self) {
68 self.0.abort();
69 }
70 pub fn signal(&self) -> AbortSignal {
71 AbortSignal(self.0.signal())
72 }
73 }
74 impl Default for AbortController {
75 fn default() -> Self {
76 Self::new()
77 }
78 }
79 impl From<web_sys::AbortSignal> for AbortSignal {
80 fn from(signal: web_sys::AbortSignal) -> Self {
81 Self(signal)
82 }
83 }
84 impl AbortSignal {
85 pub fn as_web(&self) -> &web_sys::AbortSignal {
86 &self.0
87 }
88 pub fn aborted(&self) -> bool {
89 self.0.aborted()
90 }
91 pub async fn cancelled(&self) {
92 if self.aborted() {
93 return;
94 }
95 let (sender, receiver) = oneshot::channel();
96 let _listener = gloo_events::EventListener::once(&self.0, "abort", move |_| {
98 let _ = sender.send(());
99 });
100 let _ = receiver.await;
101 }
102 }
103 impl WasmDescribe for AbortSignal {
104 fn describe() {
105 <web_sys::AbortSignal as WasmDescribe>::describe();
106 }
107 }
108 impl FromWasmAbi for AbortSignal {
109 type Abi = <web_sys::AbortSignal as FromWasmAbi>::Abi;
110 unsafe fn from_abi(value: Self::Abi) -> Self {
111 Self(unsafe { <web_sys::AbortSignal as FromWasmAbi>::from_abi(value) })
112 }
113 }
114 impl OptionFromWasmAbi for AbortSignal {
115 fn is_none(value: &Self::Abi) -> bool {
116 <web_sys::AbortSignal as OptionFromWasmAbi>::is_none(value)
117 }
118 }
119}
120pub use platform::{AbortController, AbortSignal};
121
122impl AbortSignal {
123 pub async fn until<T>(&self, future: impl Future<Output = T>) -> Result<T, Aborted> {
125 match select(pin!(self.cancelled()), pin!(future)).await {
126 Either::Left(_) => Err(Aborted),
127 Either::Right((value, _)) => Ok(value),
128 }
129 }
130}
131
132#[cfg(all(test, not(target_arch = "wasm32")))]
133mod tests {
134 use super::*;
135 use futures::{executor::block_on, FutureExt};
136 #[test]
137 fn cancellation_is_shared_idempotent_and_one_way() {
138 let parent = AbortController::default();
139 let child = AbortController::child_of(&parent.signal());
140 let sibling = AbortController::child_of(&parent.signal());
141 child.abort();
142 child.abort();
143 assert!(child.signal().aborted());
144 assert!(!parent.signal().aborted());
145 assert!(!sibling.signal().aborted());
146 parent.abort();
147 assert!(sibling.signal().aborted());
148 assert!(AbortController::child_of(&parent.signal())
149 .signal()
150 .aborted());
151 }
152 #[test]
153 fn precancellation_skips_work_and_dropping_controller_does_not_cancel() {
154 let controller = AbortController::default();
155 controller.abort();
156 let signal = controller.signal();
157 assert_eq!(
158 block_on(signal.until(async { panic!("must not run") })),
159 Err(Aborted)
160 );
161 let signal = AbortController::default().signal();
162 assert!(signal.cancelled().now_or_never().is_none());
163 }
164 #[test]
165 fn wakes_all_waiters_and_drops_non_send_work() {
166 let controller = AbortController::default();
167 let signal = controller.signal();
168 let owned = std::rc::Rc::new(());
169 let work = owned.clone();
170 let mut first = Box::pin(signal.until(async move {
171 let _guard = work;
172 futures::future::pending::<()>().await;
173 }));
174 assert!(first.as_mut().now_or_never().is_none());
175 let mut second = Box::pin(signal.cancelled());
176 assert!(second.as_mut().now_or_never().is_none());
177 controller.abort();
178 assert_eq!(block_on(first), Err(Aborted));
179 block_on(second);
180 assert_eq!(std::rc::Rc::strong_count(&owned), 1);
181 }
182}