pub struct CancellationToken { /* private fields */ }Expand description
A token which can be used to signal a cancellation request to one or more tasks.
Tasks call cancelled to get a future that completes
when cancellation is requested, and cancel requests it.
A clone is another handle to the same token: cancelling one cancels all.
A child_token is cancelled with its parent, and can
be cancelled alone.
§The tree
A token is an Arc of a node. A node holds its own cancelled flag (an
Event, which also wakes the waiters), a strong handle to
its parent node, and, once it has a child, a registry of its children
that holds a weak handle to each of them.
- A parent whose tokens are all gone stays alive as long as any child is, as a relay: cancelling a grandparent still reaches the grandchildren. A node nobody can cancel or wait on any more is freed.
- Memory is bounded by the live tokens. A node removes itself from its parent’s registry when it is freed, so the registry holds only the children that exist.
- Freeing a node walks up the chain of ancestors it was the last owner of, in a loop and not by recursion, so a chain of a million nested tokens can be dropped without overflowing the stack.
§Cancelling
cancel sets the node’s flag, which wakes its waiters, and then walks the
whole subtree with an explicit stack: it sets the flag of every descendant
that is not set yet, and descends into every descendant, those some other
call already set included. So:
- When any
cancelcall returns, the token it was called on and every descendant that existed when the call began (itschild_tokenhad completed) are cancelled, and their waiters woken, whether this call or another one set the flag first. A call on an already cancelled token still walks its subtree, so it too returns with the subtree cancelled. - A descendant being created at the same moment is cancelled by the time both calls have returned: either the new child sees its parent cancelled and cancels itself, or the walk sees the child.
- The price is the walk: every call visits every descendant once, so concurrent or repeated calls over one tree each pay for it. Cancel a large tree once.
- Cancellation is not atomic over the tree: while a call runs, another thread can see a child cancelled and then a sibling not yet cancelled.
§Progress
- Wait-free:
is_cancelled(one load),Clone, dropping a token that is not the last owner of its node,cancelledandcancelled_ownedthemselves (they only build the future), and a poll of the future once the token is cancelled (one load). - Lock-free:
canceland the first poll of a future of an uncancelled token. Setting a flag is a lock-free queue operation, and the visit of the descendants reads each registry with the map’s iterator, which restarts a bucket whose walk was disturbed by a concurrent update. Acancelnever waits for anothercancel: each walks the subtree on its own. The time it takes grows with the number of descendants, whether or not they were cancelled already. - Neither wait-free nor lock-free:
child_tokenand dropping the last owner of a node. They insert into or remove from the parent’s registry (a CAS that retries when another thread changed the same bucket), and the registry is a map that grows past 48 children: a write that meets the resize in progress waits for the thread resizing it.
Waking a task calls its Waker, which is the
executor’s and is not counted.
use nuclei::sync::CancellationToken;
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, Waker};
let token = CancellationToken::new();
let clone = token.clone();
let mut cx = Context::from_waker(Waker::noop());
let mut cancelled = pin!(clone.cancelled());
assert_eq!(cancelled.as_mut().poll(&mut cx), Poll::Pending);
token.cancel();
assert_eq!(cancelled.as_mut().poll(&mut cx), Poll::Ready(()));Implementations§
Source§impl CancellationToken
impl CancellationToken
Sourcepub fn new() -> CancellationToken
pub fn new() -> CancellationToken
Creates a new token in the non-cancelled state.
Sourcepub fn child_token(&self) -> CancellationToken
pub fn child_token(&self) -> CancellationToken
Creates a token which is cancelled whenever this token is. Unlike a clone, cancelling the child does not cancel this token.
If this token is already cancelled, the child is returned cancelled.
Neither wait-free nor lock-free: it inserts into this token’s registry of children; see the type docs.
use nuclei::sync::CancellationToken;
let token = CancellationToken::new();
let child = token.child_token();
child.cancel();
assert!(child.is_cancelled());
assert!(!token.is_cancelled());
token.cancel();
assert!(token.child_token().is_cancelled());Sourcepub fn cancel(&self)
pub fn cancel(&self)
Cancels this token and every token derived from it, waking all tasks waiting for cancellation.
When the call returns, this token and every descendant that existed
when it began are cancelled, even if another call cancelled some of
them first: every call walks the whole subtree. Cancellation is not
atomic over the tree, though: a thread running alongside the call can
see true from is_cancelled on one child and
then false on another. See the type docs.
Lock-free.
use nuclei::sync::CancellationToken;
let token = CancellationToken::new();
let child = token.child_token();
let grandchild = child.child_token();
token.cancel();
assert!(child.is_cancelled() && grandchild.is_cancelled());Sourcepub fn is_cancelled(&self) -> bool
pub fn is_cancelled(&self) -> bool
Whether the token is cancelled. One atomic load.
use nuclei::sync::CancellationToken;
let token = CancellationToken::new();
assert!(!token.is_cancelled());
token.cancel();
assert!(token.is_cancelled());Sourcepub fn cancelled(&self) -> WaitForCancellationFuture<'_> ⓘ
pub fn cancelled(&self) -> WaitForCancellationFuture<'_> ⓘ
A future that completes when cancellation is requested.
Equivalent to async fn cancelled(&self). It completes at once if the
token is already cancelled.
§Cancel safety
Cancel safe: dropping the future withdraws its wait and nothing else.
use nuclei::sync::CancellationToken;
use std::future::Future;
use std::pin::pin;
use std::task::{Context, Poll, Waker};
let token = CancellationToken::new();
token.cancel();
let mut cx = Context::from_waker(Waker::noop());
assert_eq!(pin!(token.cancelled()).poll(&mut cx), Poll::Ready(()));Sourcepub fn cancelled_owned(self) -> WaitForCancellationFutureOwned ⓘ
pub fn cancelled_owned(self) -> WaitForCancellationFutureOwned ⓘ
A future that completes when cancellation is requested, and owns the token.
Equivalent to async fn cancelled_owned(self). It completes at once
if the token is already cancelled.
§Cancel safety
Cancel safe.
Sourcepub fn drop_guard(self) -> DropGuard
pub fn drop_guard(self) -> DropGuard
Creates a DropGuard for this token: it cancels the token (and its
children) when dropped, unless disarmed.
use nuclei::sync::CancellationToken;
let token = CancellationToken::new();
drop(token.clone().drop_guard());
assert!(token.is_cancelled());Sourcepub fn drop_guard_ref(&self) -> DropGuardRef<'_>
pub fn drop_guard_ref(&self) -> DropGuardRef<'_>
Creates a DropGuardRef for this token: the borrowed counterpart of
drop_guard.
Sourcepub async fn run_until_cancelled<F>(&self, fut: F) -> Option<F::Output>where
F: Future,
pub async fn run_until_cancelled<F>(&self, fut: F) -> Option<F::Output>where
F: Future,
Runs fut to completion and returns its output, unless the token is
cancelled first: then it returns None and drops fut.
§Fairness
On an already cancelled token this returns None without polling
fut. After that, when fut completes and the token is cancelled
together, the output of fut wins.
§Cancel safety
Cancel safe only if fut is.
Sourcepub async fn run_until_cancelled_owned<F>(self, fut: F) -> Option<F::Output>where
F: Future,
pub async fn run_until_cancelled_owned<F>(self, fut: F) -> Option<F::Output>where
F: Future,
Like run_until_cancelled, taking the
token by value: the returned future owns it.
Trait Implementations§
Source§impl Clone for CancellationToken
impl Clone for CancellationToken
Source§impl Debug for CancellationToken
impl Debug for CancellationToken
Source§impl Default for CancellationToken
impl Default for CancellationToken
Source§fn default() -> CancellationToken
fn default() -> CancellationToken
impl Eq for CancellationToken
Source§impl Hash for CancellationToken
impl Hash for CancellationToken
Source§impl PartialEq for CancellationToken
impl PartialEq for CancellationToken
Source§fn eq(&self, other: &CancellationToken) -> bool
fn eq(&self, other: &CancellationToken) -> bool
Whether two tokens are equal in terms of their cancellation: cancelling one always cancels the other and vice versa. That holds for clones only, not for a parent and its child.