stop_token/lib.rs
1//! Cooperative cancellation for async Rust.
2//!
3//! # Status
4//!
5//! Experimental. The library works as is, breaking changes will bump major
6//! version, but there are no guarantees of long-term support.
7//!
8//! # Motivation
9//!
10//! Rust futures come with a build-in cancellation mechanism: dropping a future
11//! prevents any further progress of the future. This is a *hard* cancellation
12//! mechanism, meaning that the future can potentially be cancelled at any
13//! `.await` expression.
14//!
15//! Sometimes, you need are more fine-grained cancellation mechanism. Imagine a
16//! chat server that relays messages to peers. Sending a single message
17//! potentially needs several writes on the socket object. That means that, if
18//! we use hard-cancellation for client connections, a connection can be
19//! abruptly terminated mid-message (even mid-emoji, if we are especially
20//! unlucky). What we need here is cooperative cancellation: client connection
21//! should be gracefully shutdown *between* the messages.
22//!
23//! More generally, if you have an event processing loop like
24//!
25//! ```ignore
26//! while let Some(event) = work.next().await {
27//! process_event(event).await
28//! }
29//! ```
30//!
31//! you usually want to maintain an invariant that each event is either fully
32//! processed or not processed at all. If you need to terminate this loop early,
33//! you want to do this *between* iteration.
34//!
35//! # Usage
36//!
37//! You can use this crate to create a deadline received through a
38//! [`StopToken`]. You can think of a `StopSource` + `StopToken` as a
39//! single-producer, multi-consumer channel that receives a single message to
40//! "stop" when the producer is dropped:
41//!
42//! ```
43//! use async_std::prelude::*;
44//! use async_std::{stream, task};
45//!
46//! use stop_token::prelude::*;
47//! use stop_token::StopSource;
48//!
49//! use std::time::Duration;
50//!
51//! #[async_std::main]
52//! async fn main() {
53//! // Create a stop source and generate a token.
54//! let src = StopSource::new();
55//! let stop = src.token();
56//!
57//! // When stop source is dropped, the loop will stop.
58//! // Move the source to a task, and drop it after 100 millis.
59//! task::spawn(async move {
60//! task::sleep(Duration::from_millis(100)).await;
61//! drop(src);
62//! });
63//!
64//! // Create a stream that generates numbers until
65//! // it receives a signal it needs to stop.
66//! let mut work = stream::repeat(12u8).timeout_at(stop);
67//!
68//! // Loop over each item in the stream.
69//! while let Some(Ok(ev)) = work.next().await {
70//! println!("{}", ev);
71//! }
72//! }
73//! ```
74//!
75//! Or `Instant` to create a `time`-based deadline:
76//!
77//! ```
78//! # #![allow(dead_code)]
79//! use async_std::prelude::*;
80//! use async_std::stream;
81//!
82//! use stop_token::prelude::*;
83//!
84//! use std::time::{Duration, Instant};
85//!
86//! # #[cfg(feature = "tokio")]
87//! # fn main() {}
88//! # #[cfg(not(feature = "tokio"))]
89//! #[async_std::main]
90//! async fn main() {
91//! // Create a stream that generates numbers for 100 millis.
92//! let stop = Instant::now() + Duration::from_millis(100);
93//! let mut work = stream::repeat(12u8).timeout_at(stop);
94//!
95//! // Loop over each item in the stream.
96//! while let Some(Ok(ev)) = work.next().await {
97//! println!("{}", ev);
98//! }
99//! }
100//! ```
101//!
102//! # Features
103//!
104//! The `time` submodule is empty when no features are enabled. To implement `Into<Deadline>`
105//! for `Instant` you can enable one of the following features:
106//!
107//! - `async-io`: for use with the `async-std` or `smol` runtimes.
108//! - `tokio`: for use with the `tokio` runtime.
109//!
110//! # Lineage
111//!
112//! The cancellation system is a subset of `C#` [`CancellationToken / CancellationTokenSource`](https://docs.microsoft.com/en-us/dotnet/standard/threading/cancellation-in-managed-threads).
113//! The `StopToken / StopTokenSource` terminology is borrowed from [C++ paper P0660](https://wg21.link/p0660).
114
115#![forbid(unsafe_code)]
116#![deny(missing_debug_implementations, nonstandard_style, rust_2018_idioms)]
117#![warn(missing_docs, future_incompatible, unreachable_pub)]
118
119pub mod future;
120pub mod stream;
121
122#[cfg(any(feature = "async-io", feature = "docs"))]
123pub mod async_io;
124#[cfg(feature = "async_std")]
125pub mod async_std;
126#[cfg(feature = "tokio")]
127pub mod tokio;
128
129mod deadline;
130mod stop_source;
131
132pub use deadline::{Deadline, TimedOutError};
133pub use stop_source::{StopSource, StopToken};
134
135/// A prelude for `stop-token`.
136pub mod prelude {
137 pub use crate::future::FutureExt as _;
138 pub use crate::stream::StreamExt as _;
139}