dataflow_rs/base/
cancel.rs1use std::fmt;
5use std::sync::Arc;
6use std::sync::atomic::{AtomicBool, Ordering};
7#[cfg(not(target_arch = "wasm32"))]
8use {std::pin::Pin, std::pin::pin, tokio::sync::Notify};
9
10#[derive(Clone, Default)]
29pub struct CancelToken {
30 inner: Arc<Inner>,
31}
32
33#[derive(Default)]
35struct Inner {
36 flag: AtomicBool,
37 parent: Option<Arc<Self>>,
38 #[cfg(not(target_arch = "wasm32"))]
41 waiters: Notify,
42}
43
44impl CancelToken {
45 pub fn new() -> Self {
47 Self::default()
48 }
49
50 #[cfg_attr(
52 not(target_arch = "wasm32"),
53 doc = "Every task awaiting [`cancelled`](Self::cancelled) on them is woken."
54 )]
55 pub fn cancel(&self) {
56 self.inner.flag.store(true, Ordering::Release);
57 #[cfg(not(target_arch = "wasm32"))]
58 self.inner.waiters.notify_waiters();
59 }
60
61 pub fn is_cancelled(&self) -> bool {
63 let mut at = Some(&self.inner);
64 while let Some(inner) = at {
65 if inner.flag.load(Ordering::Acquire) {
66 return true;
67 }
68 at = inner.parent.as_ref();
69 }
70 false
71 }
72
73 #[must_use]
76 pub fn child(&self) -> Self {
77 Self {
78 inner: Arc::new(Inner {
79 parent: Some(Arc::clone(&self.inner)),
80 ..Inner::default()
81 }),
82 }
83 }
84
85 #[cfg(not(target_arch = "wasm32"))]
90 pub async fn cancelled(&self) {
91 tripped(&self.inner).await;
92 }
93}
94
95#[cfg(not(target_arch = "wasm32"))]
100async fn tripped(inner: &Inner) {
101 let mut own = pin!(inner.waiters.notified());
102 own.as_mut().enable();
103 if inner.flag.load(Ordering::Acquire) {
104 return;
105 }
106 match &inner.parent {
107 None => own.await,
108 Some(parent) => {
109 futures_util::future::select(own, boxed(parent)).await;
110 }
111 }
112}
113
114#[cfg(not(target_arch = "wasm32"))]
117fn boxed(inner: &Inner) -> Pin<Box<dyn Future<Output = ()> + Send + '_>> {
118 Box::pin(tripped(inner))
119}
120
121impl fmt::Debug for CancelToken {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
124 f.debug_struct("CancelToken")
125 .field("cancelled", &self.is_cancelled())
126 .finish_non_exhaustive()
127 }
128}
129
130#[cfg(test)]
131mod tests {
132 use super::*;
133
134 #[test]
135 fn a_new_token_is_not_cancelled_and_cancel_trips_every_clone() {
136 let token = CancelToken::new();
137 let clone = token.clone();
138 assert!(!token.is_cancelled());
139 clone.cancel();
140 assert!(token.is_cancelled() && clone.is_cancelled());
141 }
142
143 #[test]
144 fn a_child_trips_with_any_ancestor_and_alone_without_them() {
145 let root = CancelToken::new();
146 let child = root.child();
147 let grandchild = child.child();
148 let sibling = root.child();
149 grandchild.cancel();
150 assert!(grandchild.is_cancelled());
151 assert!(!child.is_cancelled() && !root.is_cancelled() && !sibling.is_cancelled());
152 root.cancel();
153 assert!(child.is_cancelled() && sibling.is_cancelled());
154 assert!(root.child().is_cancelled(), "a child of a cancelled token");
155 }
156
157 #[cfg(not(target_arch = "wasm32"))]
159 fn poll_once<F: Future>(future: Pin<&mut F>) -> bool {
160 let waker = std::task::Waker::noop();
161 future
162 .poll(&mut std::task::Context::from_waker(waker))
163 .is_ready()
164 }
165
166 #[test]
167 #[cfg(not(target_arch = "wasm32"))]
168 fn cancelled_is_pending_until_the_token_or_an_ancestor_is_cancelled() {
169 let root = CancelToken::new();
170 let child = root.child();
171 let mut on_child = pin!(child.cancelled());
172 let mut on_root = pin!(root.cancelled());
173 assert!(!poll_once(on_child.as_mut()) && !poll_once(on_root.as_mut()));
174 root.cancel();
175 assert!(poll_once(on_child.as_mut()) && poll_once(on_root.as_mut()));
176 assert!(poll_once(pin!(child.child().cancelled())));
178 }
179
180 #[test]
181 fn debug_shows_whether_it_is_cancelled() {
182 let token = CancelToken::new();
183 assert_eq!(format!("{token:?}"), "CancelToken { cancelled: false, .. }");
184 }
185}