//! Asynchronous green-threads. //! //! ## What are Tasks? //! //! A _task_ is a light weight, non-blocking unit of execution. A task is similar //! to an OS thread, but rather than being managed by the OS scheduler, they are //! managed by the [Tokio runtime][rt]. Another name for this general pattern is //! [green threads]. If you are familiar with [Go's goroutines], [Kotlin's //! coroutines], or [Erlang's processes], you can think of Tokio's tasks as //! something similar. //! //! Key points about tasks include: //! //! * Tasks are **light weight**. Because tasks are scheduled by the Tokio //! runtime rather than the operating system, creating new tasks or switching //! between tasks does not require a context switch and has fairly low //! overhead. Creating, running, and destroying large numbers of tasks is //! quite cheap, especially compared to OS threads. //! //! * Tasks are scheduled **cooperatively**. Most operating systems implement //! _preemptive multitasking_. This is a scheduling technique where the //! operating system allows each thread to run for a period of time, and then //! _preempts_ it, temporarily pausing that thread and switching to another. //! Tasks, on the other hand, implement _cooperative multitasking_. In //! cooperative multitasking, a task is allowed to run until it _yields_, //! indicating to the Tokio runtime's scheduler that it cannot currently //! continue executing. When a task yields, the Tokio runtime switches to //! executing the next task. //! //! * Tasks are **non-blocking**. Typically, when an OS thread performs I/O or //! must synchronize with another thread, it _blocks_, allowing the OS to //! schedule another thread. When a task cannot continue executing, it must //! yield instead, allowing the Tokio runtime to schedule another task. Tasks //! should generally not perform system calls or other operations that could //! block a thread, as this would prevent other tasks running on the same //! thread from executing as well. Instead, this module provides APIs for //! running blocking operations in an asynchronous context. //! //! [rt]: crate::runtime //! [green threads]: https://en.wikipedia.org/wiki/Green_threads //! [Go's goroutines]: https://tour.golang.org/concurrency/1 //! [Kotlin's coroutines]: https://kotlinlang.org/docs/reference/coroutines-overview.html //! [Erlang's processes]: http://erlang.org/doc/getting_started/conc_prog.html#processes //! //! ## Working with Tasks //! //! This module provides the following APIs for working with tasks: //! //! ### Spawning //! //! Perhaps the most important function in this module is [`task::spawn`]. This //! function can be thought of as an async equivalent to the standard library's //! [`thread::spawn`][`std::thread::spawn`]. It takes an `async` block or other //! [future], and creates a new task to run that work concurrently: //! //! ``` //! use tokio::task; //! //! # async fn doc() { //! task::spawn(async { //! // perform some work here... //! }); //! # } //! ``` //! //! Like [`std::thread::spawn`], `task::spawn` returns a [`JoinHandle`] struct. //! A `JoinHandle` is itself a future which may be used to await the output of //! the spawned task. For example: //! //! ``` //! use tokio::task; //! //! # #[tokio::main] async fn main() -> Result<(), Box> { //! let join = task::spawn(async { //! // ... //! "hello world!" //! }); //! //! // ... //! //! // Await the result of the spawned task. //! let result = join.await?; //! assert_eq!(result, "hello world!"); //! # Ok(()) //! # } //! ``` //! //! Again, like `std::thread`'s [`JoinHandle` type][thread_join], if the spawned //! task panics, awaiting its `JoinHandle` will return a [`JoinError`]. For //! example: //! //! ``` //! use tokio::task; //! //! # #[tokio::main] async fn main() { //! let join = task::spawn(async { //! panic!("something bad happened!") //! }); //! //! // The returned result indicates that the task failed. //! assert!(join.await.is_err()); //! # } //! ``` //! //! `spawn`, `JoinHandle`, and `JoinError` are present when the "rt" //! feature flag is enabled. //! //! [`task::spawn`]: crate::task::spawn() //! [future]: std::future::Future //! [`std::thread::spawn`]: std::thread::spawn //! [`JoinHandle`]: crate::task::JoinHandle //! [thread_join]: std::thread::JoinHandle //! [`JoinError`]: crate::task::JoinError //! //! ### Blocking and Yielding //! //! As we discussed above, code running in asynchronous tasks should not perform //! operations that can block. A blocking operation performed in a task running //! on a thread that is also running other tasks would block the entire thread, //! preventing other tasks from running. //! //! Instead, Tokio provides two APIs for running blocking operations in an //! asynchronous context: [`task::spawn_blocking`] and [`task::block_in_place`]. //! //! Be aware that if you call a non-async method from async code, that non-async //! method is still inside the asynchronous context, so you should also avoid //! blocking operations there. This includes destructors of objects destroyed in //! async code. //! //! #### spawn_blocking //! //! The `task::spawn_blocking` function is similar to the `task::spawn` function //! discussed in the previous section, but rather than spawning an //! _non-blocking_ future on the Tokio runtime, it instead spawns a //! _blocking_ function on a dedicated thread pool for blocking tasks. For //! example: //! //! ``` //! use tokio::task; //! //! # async fn docs() { //! task::spawn_blocking(|| { //! // do some compute-heavy work or call synchronous code //! }); //! # } //! ``` //! //! Just like `task::spawn`, `task::spawn_blocking` returns a `JoinHandle` //! which we can use to await the result of the blocking operation: //! //! ```rust //! # use tokio::task; //! # async fn docs() -> Result<(), Box>{ //! let join = task::spawn_blocking(|| { //! // do some compute-heavy work or call synchronous code //! "blocking completed" //! }); //! //! let result = join.await?; //! assert_eq!(result, "blocking completed"); //! # Ok(()) //! # } //! ``` //! //! #### block_in_place //! //! When using the [multi-threaded runtime][rt-multi-thread], the [`task::block_in_place`] //! function is also available. Like `task::spawn_blocking`, this function //! allows running a blocking operation from an asynchronous context. Unlike //! `spawn_blocking`, however, `block_in_place` works by transitioning the //! _current_ worker thread to a blocking thread, moving other tasks running on //! that thread to another worker thread. This can improve performance by avoiding //! context switches. //! //! For example: //! //! ``` //! use tokio::task; //! //! # async fn docs() { //! let result = task::block_in_place(|| { //! // do some compute-heavy work or call synchronous code //! "blocking completed" //! }); //! //! assert_eq!(result, "blocking completed"); //! # } //! ``` //! //! #### yield_now //! //! In addition, this module provides a [`task::yield_now`] async function //! that is analogous to the standard library's [`thread::yield_now`]. Calling //! and `await`ing this function will cause the current task to yield to the //! Tokio runtime's scheduler, allowing other tasks to be //! scheduled. Eventually, the yielding task will be polled again, allowing it //! to execute. For example: //! //! ```rust //! use tokio::task; //! //! # #[tokio::main] async fn main() { //! async { //! task::spawn(async { //! // ... //! println!("spawned task done!") //! }); //! //! // Yield, allowing the newly-spawned task to execute first. //! task::yield_now().await; //! println!("main task done!"); //! } //! # .await; //! # } //! ``` //! //! ### 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(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}; mod blocking; pub use blocking::spawn_blocking; mod spawn; pub use spawn::spawn; cfg_rt_multi_thread! { pub use blocking::block_in_place; } mod yield_now; pub use yield_now::yield_now; mod local; pub use local::{spawn_local, LocalSet}; mod task_local; pub use task_local::LocalKey; mod unconstrained; pub use unconstrained::{unconstrained, Unconstrained}; cfg_trace! { mod builder; pub use builder::Builder; } }