aboutsummaryrefslogtreecommitdiff
path: root/src/task/mod.rs
diff options
context:
space:
mode:
authorJeff Vander Stoep <jeffv@google.com>2021-04-21 15:58:31 +0200
committerJeff Vander Stoep <jeffv@google.com>2021-04-21 15:59:26 +0200
commit28f5548b7bc0734d62d4624bb87b2bc60c78d49a (patch)
treec39809fba43deb9ee717b07b2afaeb71ef36ca59 /src/task/mod.rs
parent714f4488dbef671e82a443b31cc99ea7216dcebd (diff)
downloadtokio-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.rs58
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};
}