nuclei/sync/cancellation.rs
1//! A token that signals a cancellation request to one or more tasks, with
2//! child tokens that follow their parent.
3//!
4//! [`CancellationToken::cancel`] marks the token and every token derived
5//! from it by [`child_token`](CancellationToken::child_token), and wakes
6//! every task waiting in [`cancelled`](CancellationToken::cancelled). A child
7//! can be cancelled alone; cancelling a child never cancels its parent. The
8//! API is the established async cancellation-token API; nothing here needs a
9//! runtime.
10//!
11//! ```
12//! use nuclei::sync::CancellationToken;
13//!
14//! let parent = CancellationToken::new();
15//! let child = parent.child_token();
16//! assert!(!child.is_cancelled());
17//! parent.cancel();
18//! assert!(child.is_cancelled());
19//! ```
20//!
21//! The tree, what `cancel` promises and the progress guarantee of each
22//! operation are documented on [`CancellationToken`], where a reader of the
23//! public API finds them. This module's docs keep the argument for the
24//! memory ordering of the tree.
25//!
26//! # Memory ordering of the tree
27//!
28//! `child_token` and `cancel` race: a child added while the parent is being
29//! cancelled must end up cancelled. The two sides use the same pattern, a
30//! store followed by a `SeqCst` fence followed by a read of the other's store
31//! (Dekker):
32//!
33//! - `child_token` publishes the child in the parent's registry, fences, and
34//! then reads the parent's flag.
35//! - `cancel` sets the flag (unless an earlier call did), fences, and then
36//! iterates the registry.
37//!
38//! The fences are ordered one way or the other in the single `SeqCst` order.
39//! If the child's fence comes first, everything before it (the registry
40//! insert) is visible to the reads after the canceller's fence, so the
41//! iteration, which starts after that fence, yields the child: the registry
42//! guarantees that an entry present from the start of an iteration to its end
43//! is yielded exactly once. If the canceller's fence comes first, the flag
44//! set before it is visible to the read after the child's fence, so the child
45//! sees its parent cancelled and cancels itself. Either way the child is
46//! cancelled. The same argument covers the registry itself being created
47//! lazily, since its publication is a store before the child's fence.
48//!
49//! A `cancel` that finds the flag already set stores nothing, but it fences
50//! and iterates all the same, and that is what its own guarantee rests on:
51//! a child whose `child_token` had completed before the call is in the
52//! registry for the whole walk, so the registry yields it. A child inserted
53//! while the walk runs is the race above, and one of the two sides covers
54//! it: the call that stored the flag, or the child, which reads the flag
55//! after its own fence. So a child being created meanwhile is cancelled once
56//! both the `cancel` and its own `child_token` have returned.
57//!
58//! Under `--cfg loom` the registry is a loom `Mutex` around a `BTreeMap` (the
59//! map in the real build is a lock-free kovan map that loom cannot
60//! instrument; the ordered map keeps the model deterministic), and
61//! [`Arc`]/[`Weak`] are the small models in `rc`, since loom has neither a
62//! weak reference nor `Arc::into_inner`. So the loom models check the
63//! protocol around the registry (the flag, the wakeups, the order of insert,
64//! visit and removal, the ownership of nodes) with a registry that is
65//! atomic by itself; the argument above is what covers the real registry.
66
67use core::fmt;
68use core::future::Future;
69use core::hash::{Hash, Hasher};
70
71mod guard;
72mod node;
73#[cfg(all(loom, test))]
74mod rc;
75mod wait;
76
77pub use guard::{DropGuard, DropGuardRef};
78pub use wait::{WaitForCancellationFuture, WaitForCancellationFutureOwned};
79
80use node::Node;
81use wait::RunUntilCancelled;
82
83#[cfg(not(all(loom, test)))]
84use std::sync::{Arc, Weak};
85
86#[cfg(all(loom, test))]
87use self::rc::{Arc, Weak};
88
89/// A token which can be used to signal a cancellation request to one or more
90/// tasks.
91///
92/// Tasks call [`cancelled`](Self::cancelled) to get a future that completes
93/// when cancellation is requested, and [`cancel`](Self::cancel) requests it.
94/// A clone is another handle to the same token: cancelling one cancels all.
95/// A [`child_token`](Self::child_token) is cancelled with its parent, and can
96/// be cancelled alone.
97///
98/// # The tree
99///
100/// A token is an `Arc` of a node. A node holds its own cancelled flag (an
101/// [`Event`](crate::sync::Event), which also wakes the waiters), a *strong* handle to
102/// its parent node, and, once it has a child, a registry of its children
103/// that holds a *weak* handle to each of them.
104///
105/// - A parent whose tokens are all gone stays alive as long as any child is,
106/// as a relay: cancelling a grandparent still reaches the grandchildren. A
107/// node nobody can cancel or wait on any more is freed.
108/// - Memory is bounded by the live tokens. A node removes itself from its
109/// parent's registry when it is freed, so the registry holds only the
110/// children that exist.
111/// - Freeing a node walks up the chain of ancestors it was the last owner of,
112/// in a loop and not by recursion, so a chain of a million nested tokens
113/// can be dropped without overflowing the stack.
114///
115/// # Cancelling
116///
117/// `cancel` sets the node's flag, which wakes its waiters, and then walks the
118/// whole subtree with an explicit stack: it sets the flag of every descendant
119/// that is not set yet, and descends into every descendant, those some other
120/// call already set included. So:
121///
122/// - When any `cancel` call returns, the token it was called on and every
123/// descendant that existed when the call began (its `child_token` had
124/// completed) are cancelled, and their waiters woken, whether this call or
125/// another one set the flag first. A call on an already cancelled token
126/// still walks its subtree, so it too returns with the subtree cancelled.
127/// - A descendant being created at the same moment is cancelled by the time
128/// both calls have returned: either the new child sees its parent
129/// cancelled and cancels itself, or the walk sees the child.
130/// - The price is the walk: every call visits every descendant once, so
131/// concurrent or repeated calls over one tree each pay for it. Cancel a
132/// large tree once.
133/// - Cancellation is not atomic over the tree: while a call runs, another
134/// thread can see a child cancelled and then a sibling not yet cancelled.
135///
136/// # Progress
137///
138/// - Wait-free: [`is_cancelled`](CancellationToken::is_cancelled) (one load),
139/// [`Clone`], dropping a token that is not the last owner of its node,
140/// [`cancelled`](CancellationToken::cancelled) and
141/// [`cancelled_owned`](CancellationToken::cancelled_owned) themselves
142/// (they only build the future), and a poll of the future once the token is
143/// cancelled (one load).
144/// - Lock-free: [`cancel`](CancellationToken::cancel) and the first poll of a
145/// future of an uncancelled token. Setting a flag is a lock-free queue
146/// operation, and the visit of the descendants reads each registry with
147/// the map's iterator, which restarts a bucket whose walk was disturbed
148/// by a concurrent update. A `cancel` never waits for another `cancel`:
149/// each walks the subtree on its own. The time it takes grows with the
150/// number of descendants, whether or not they were cancelled already.
151/// - Neither wait-free nor lock-free:
152/// [`child_token`](CancellationToken::child_token) and dropping the last
153/// owner of a node. They insert into or remove from the parent's registry
154/// (a CAS that retries when another thread changed the same bucket), and
155/// the registry is a map that grows past 48 children: a write that meets
156/// the resize in progress waits for the thread resizing it.
157///
158/// Waking a task calls its [`Waker`](std::task::Waker), which is the
159/// executor's and is not counted.
160///
161/// ```
162/// use nuclei::sync::CancellationToken;
163/// use std::future::Future;
164/// use std::pin::pin;
165/// use std::task::{Context, Poll, Waker};
166///
167/// let token = CancellationToken::new();
168/// let clone = token.clone();
169/// let mut cx = Context::from_waker(Waker::noop());
170/// let mut cancelled = pin!(clone.cancelled());
171/// assert_eq!(cancelled.as_mut().poll(&mut cx), Poll::Pending);
172/// token.cancel();
173/// assert_eq!(cancelled.as_mut().poll(&mut cx), Poll::Ready(()));
174/// ```
175pub struct CancellationToken {
176 node: Arc<Node>,
177}
178
179impl std::panic::UnwindSafe for CancellationToken {}
180impl std::panic::RefUnwindSafe for CancellationToken {}
181
182impl fmt::Debug for CancellationToken {
183 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
184 f.debug_struct("CancellationToken")
185 .field("is_cancelled", &self.is_cancelled())
186 .finish()
187 }
188}
189
190impl Clone for CancellationToken {
191 /// Creates a clone of the token which gets cancelled whenever the
192 /// current token gets cancelled, and vice versa.
193 fn clone(&self) -> Self {
194 CancellationToken {
195 node: Arc::clone(&self.node),
196 }
197 }
198}
199
200impl PartialEq for CancellationToken {
201 /// Whether two tokens are equal in terms of their cancellation: cancelling
202 /// one always cancels the other and vice versa. That holds for clones
203 /// only, not for a parent and its child.
204 fn eq(&self, other: &CancellationToken) -> bool {
205 Arc::ptr_eq(&self.node, &other.node)
206 }
207}
208
209impl Eq for CancellationToken {}
210
211impl Hash for CancellationToken {
212 fn hash<H: Hasher>(&self, state: &mut H) {
213 Arc::as_ptr(&self.node).hash(state);
214 }
215}
216
217impl Default for CancellationToken {
218 fn default() -> CancellationToken {
219 CancellationToken::new()
220 }
221}
222
223impl CancellationToken {
224 /// Creates a new token in the non-cancelled state.
225 pub fn new() -> CancellationToken {
226 CancellationToken {
227 node: Node::new(None),
228 }
229 }
230
231 /// Creates a token which is cancelled whenever this token is. Unlike a
232 /// clone, cancelling the child does not cancel this token.
233 ///
234 /// If this token is already cancelled, the child is returned cancelled.
235 ///
236 /// Neither wait-free nor lock-free: it inserts into this token's
237 /// registry of children; see the [type docs](CancellationToken).
238 ///
239 /// ```
240 /// use nuclei::sync::CancellationToken;
241 ///
242 /// let token = CancellationToken::new();
243 /// let child = token.child_token();
244 /// child.cancel();
245 /// assert!(child.is_cancelled());
246 /// assert!(!token.is_cancelled());
247 ///
248 /// token.cancel();
249 /// assert!(token.child_token().is_cancelled());
250 /// ```
251 pub fn child_token(&self) -> CancellationToken {
252 CancellationToken {
253 node: Node::child(&self.node),
254 }
255 }
256
257 /// Cancels this token and every token derived from it, waking all tasks
258 /// waiting for cancellation.
259 ///
260 /// When the call returns, this token and every descendant that existed
261 /// when it began are cancelled, even if another call cancelled some of
262 /// them first: every call walks the whole subtree. Cancellation is not
263 /// atomic over the tree, though: a thread running alongside the call can
264 /// see `true` from [`is_cancelled`](Self::is_cancelled) on one child and
265 /// then `false` on another. See the [type docs](CancellationToken).
266 ///
267 /// Lock-free.
268 ///
269 /// ```
270 /// use nuclei::sync::CancellationToken;
271 ///
272 /// let token = CancellationToken::new();
273 /// let child = token.child_token();
274 /// let grandchild = child.child_token();
275 /// token.cancel();
276 /// assert!(child.is_cancelled() && grandchild.is_cancelled());
277 /// ```
278 pub fn cancel(&self) {
279 Node::cancel(&self.node);
280 }
281
282 /// Whether the token is cancelled. One atomic load.
283 ///
284 /// ```
285 /// use nuclei::sync::CancellationToken;
286 ///
287 /// let token = CancellationToken::new();
288 /// assert!(!token.is_cancelled());
289 /// token.cancel();
290 /// assert!(token.is_cancelled());
291 /// ```
292 pub fn is_cancelled(&self) -> bool {
293 self.node.cancelled.is_set()
294 }
295
296 /// A future that completes when cancellation is requested.
297 ///
298 /// Equivalent to `async fn cancelled(&self)`. It completes at once if the
299 /// token is already cancelled.
300 ///
301 /// # Cancel safety
302 ///
303 /// Cancel safe: dropping the future withdraws its wait and nothing else.
304 ///
305 /// ```
306 /// use nuclei::sync::CancellationToken;
307 /// use std::future::Future;
308 /// use std::pin::pin;
309 /// use std::task::{Context, Poll, Waker};
310 ///
311 /// let token = CancellationToken::new();
312 /// token.cancel();
313 /// let mut cx = Context::from_waker(Waker::noop());
314 /// assert_eq!(pin!(token.cancelled()).poll(&mut cx), Poll::Ready(()));
315 /// ```
316 pub fn cancelled(&self) -> WaitForCancellationFuture<'_> {
317 WaitForCancellationFuture {
318 wait: self.node.cancelled.wait(),
319 }
320 }
321
322 /// A future that completes when cancellation is requested, and owns the
323 /// token.
324 ///
325 /// Equivalent to `async fn cancelled_owned(self)`. It completes at once
326 /// if the token is already cancelled.
327 ///
328 /// # Cancel safety
329 ///
330 /// Cancel safe.
331 pub fn cancelled_owned(self) -> WaitForCancellationFutureOwned {
332 WaitForCancellationFutureOwned::new(self)
333 }
334
335 /// Creates a [`DropGuard`] for this token: it cancels the token (and its
336 /// children) when dropped, unless disarmed.
337 ///
338 /// ```
339 /// use nuclei::sync::CancellationToken;
340 ///
341 /// let token = CancellationToken::new();
342 /// drop(token.clone().drop_guard());
343 /// assert!(token.is_cancelled());
344 /// ```
345 pub fn drop_guard(self) -> DropGuard {
346 DropGuard {
347 token: self,
348 armed: true,
349 }
350 }
351
352 /// Creates a [`DropGuardRef`] for this token: the borrowed counterpart of
353 /// [`drop_guard`](Self::drop_guard).
354 pub fn drop_guard_ref(&self) -> DropGuardRef<'_> {
355 DropGuardRef {
356 token: self,
357 armed: true,
358 }
359 }
360
361 /// Runs `fut` to completion and returns its output, unless the token is
362 /// cancelled first: then it returns `None` and drops `fut`.
363 ///
364 /// # Fairness
365 ///
366 /// On an already cancelled token this returns `None` without polling
367 /// `fut`. After that, when `fut` completes and the token is cancelled
368 /// together, the output of `fut` wins.
369 ///
370 /// # Cancel safety
371 ///
372 /// Cancel safe only if `fut` is.
373 pub async fn run_until_cancelled<F>(&self, fut: F) -> Option<F::Output>
374 where
375 F: Future,
376 {
377 if self.is_cancelled() {
378 None
379 } else {
380 RunUntilCancelled {
381 cancellation: self.cancelled(),
382 future: fut,
383 }
384 .await
385 }
386 }
387
388 /// Like [`run_until_cancelled`](Self::run_until_cancelled), taking the
389 /// token by value: the returned future owns it.
390 pub async fn run_until_cancelled_owned<F>(self, fut: F) -> Option<F::Output>
391 where
392 F: Future,
393 {
394 self.run_until_cancelled(fut).await
395 }
396}
397
398#[cfg(all(test, not(loom)))]
399mod tests;