diff options
author | Jeff Vander Stoep <jeffv@google.com> | 2021-04-21 15:58:31 +0200 |
---|---|---|
committer | Jeff Vander Stoep <jeffv@google.com> | 2021-04-21 15:59:26 +0200 |
commit | 28f5548b7bc0734d62d4624bb87b2bc60c78d49a (patch) | |
tree | c39809fba43deb9ee717b07b2afaeb71ef36ca59 /src/task/mod.rs | |
parent | 714f4488dbef671e82a443b31cc99ea7216dcebd (diff) | |
download | tokio-28f5548b7bc0734d62d4624bb87b2bc60c78d49a.tar.gz |
Update to 1.50.0
Test: atest
Change-Id: I94e6acadea178b0b957fbf853a590f155d1bd973
Diffstat (limited to 'src/task/mod.rs')
-rw-r--r-- | src/task/mod.rs | 58 |
1 files changed, 58 insertions, 0 deletions
diff --git a/src/task/mod.rs b/src/task/mod.rs index 5dc5e72..abae818 100644 --- a/src/task/mod.rs +++ b/src/task/mod.rs @@ -209,11 +209,66 @@ //! # } //! ``` //! +//! ### Cooperative scheduling +//! +//! A single call to [`poll`] on a top-level task may potentially do a lot of +//! work before it returns `Poll::Pending`. If a task runs for a long period of +//! time without yielding back to the executor, it can starve other tasks +//! waiting on that executor to execute them, or drive underlying resources. +//! Since Rust does not have a runtime, it is difficult to forcibly preempt a +//! long-running task. Instead, this module provides an opt-in mechanism for +//! futures to collaborate with the executor to avoid starvation. +//! +//! Consider a future like this one: +//! +//! ``` +//! # use tokio_stream::{Stream, StreamExt}; +//! async fn drop_all<I: Stream + Unpin>(mut input: I) { +//! while let Some(_) = input.next().await {} +//! } +//! ``` +//! +//! It may look harmless, but consider what happens under heavy load if the +//! input stream is _always_ ready. If we spawn `drop_all`, the task will never +//! yield, and will starve other tasks and resources on the same executor. +//! +//! To account for this, Tokio has explicit yield points in a number of library +//! functions, which force tasks to return to the executor periodically. +//! +//! +//! #### unconstrained +//! +//! If necessary, [`task::unconstrained`] lets you opt out a future of Tokio's cooperative +//! scheduling. When a future is wrapped with `unconstrained`, it will never be forced to yield to +//! Tokio. For example: +//! +//! ``` +//! # #[tokio::main] +//! # async fn main() { +//! use tokio::{task, sync::mpsc}; +//! +//! let fut = async { +//! let (tx, mut rx) = mpsc::unbounded_channel(); +//! +//! for i in 0..1000 { +//! let _ = tx.send(()); +//! // This will always be ready. If coop was in effect, this code would be forced to yield +//! // periodically. However, if left unconstrained, then this code will never yield. +//! rx.recv().await; +//! } +//! }; +//! +//! task::unconstrained(fut).await; +//! # } +//! ``` +//! //! [`task::spawn_blocking`]: crate::task::spawn_blocking //! [`task::block_in_place`]: crate::task::block_in_place //! [rt-multi-thread]: ../runtime/index.html#threaded-scheduler //! [`task::yield_now`]: crate::task::yield_now() //! [`thread::yield_now`]: std::thread::yield_now +//! [`task::unconstrained`]: crate::task::unconstrained() +//! [`poll`]: method@std::future::Future::poll cfg_rt! { pub use crate::runtime::task::{JoinError, JoinHandle}; @@ -236,4 +291,7 @@ cfg_rt! { mod task_local; pub use task_local::LocalKey; + + mod unconstrained; + pub use unconstrained::{unconstrained, Unconstrained}; } |