summaryrefslogtreecommitdiffstats
path: root/tokio
diff options
context:
space:
mode:
authorEliza Weisman <eliza@buoyant.io>2019-11-20 14:36:45 -0800
committerGitHub <noreply@github.com>2019-11-20 14:36:45 -0800
commitc223db35896bd6b6714d4793cd84290e573bb216 (patch)
tree6c751e3aed6f915a7a48e88365e2fe4420566135 /tokio
parent5cd665afd7b70b184b559e6407fdf645983e1314 (diff)
docs: improve `tokio::task` API documentation (#1801)
## Motivation The new `tokio::task` module is pretty lacking in API docs. ## Solution This branch adds new API docs to the `task` module, including: * Module-level docs with a summary of the differences between tasks and threads * Examples of how to use the `task` APIs in the module-level docs * More docs for `yield_now` * More docs and examples for `JoinHandle`, based on the `std::thread::JoinHandle` API docs. This branch contains commits cherry-picked from #1794 Signed-off-by: Eliza Weisman <eliza@buoyant.io>
Diffstat (limited to 'tokio')
-rw-r--r--tokio/src/task/join.rs67
-rw-r--r--tokio/src/task/mod.rs209
2 files changed, 275 insertions, 1 deletions
diff --git a/tokio/src/task/join.rs b/tokio/src/task/join.rs
index c2c59a2c..5ae93ea8 100644
--- a/tokio/src/task/join.rs
+++ b/tokio/src/task/join.rs
@@ -8,6 +8,73 @@ use std::pin::Pin;
use std::task::{Context, Poll};
/// An owned permission to join on a task (await its termination).
+///
+/// This can be thought of as the equivalent of [`std::thread::JoinHandle`] for
+/// a task rather than a thread.
+///
+/// A `JoinHandle` *detaches* the associated task when it is dropped, which
+/// means that there is no longer any handle to the task, and no way to `join`
+/// on it.
+///
+/// This `struct` is created by the [`task::spawn`] and [`task::spawn_blocking`]
+/// functions.
+///
+/// # Examples
+///
+/// Creation from [`task::spawn`]:
+///
+/// ```
+/// use tokio::task;
+///
+/// # async fn doc() {
+/// let join_handle: task::JoinHandle<_> = task::spawn(async {
+/// // some work here
+/// });
+/// # }
+/// ```
+///
+/// Creation from [`task::spawn_blocking`]:
+///
+/// ```
+/// use tokio::task;
+///
+/// # async fn doc() {
+/// let join_handle: task::JoinHandle<_> = task::spawn_blocking(|| {
+/// // some blocking work here
+/// });
+/// # }
+/// ```
+///
+/// Child being detached and outliving its parent:
+///
+/// ```no_run
+/// use tokio::task;
+/// use tokio::time;
+/// use std::time::Duration;
+///
+/// # #[tokio::main] async fn main() {
+/// let original_task = task::spawn(async {
+/// let _detached_task = task::spawn(async {
+/// // Here we sleep to make sure that the first task returns before.
+/// time::delay_for(Duration::from_millis(10)).await;
+/// // This will be called, even though the JoinHandle is dropped.
+/// println!("♫ Still alive ♫");
+/// });
+/// });
+///
+/// original_task.await.expect("The task being joined has panicked");
+/// println!("Original task is joined.");
+///
+/// // We make sure that the new task has time to run, before the main
+/// // task returns.
+///
+/// time::delay_for(Duration::from_millis(1000)).await;
+/// # }
+/// ```
+///
+/// [`task::spawn`]: crate::task::spawn()
+/// [`task::spawn_blocking`]: crate::task::spawn_blocking
+/// [`std::thread::JoinHandle`]: std::thread::JoinHandle
pub struct JoinHandle<T> {
raw: Option<RawTask>,
_p: PhantomData<T>,
diff --git a/tokio/src/task/mod.rs b/tokio/src/task/mod.rs
index 7b27ff1b..39d25ec8 100644
--- a/tokio/src/task/mod.rs
+++ b/tokio/src/task/mod.rs
@@ -1,5 +1,212 @@
//! 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 patterh 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`][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<dyn std::error::Error>> {
+//! 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-core"
+//! 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`].
+//!
+//! 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<dyn std::error::Error>>{
+//! 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(())
+//! # }
+//! ```
+//!
+//! When using the [threaded runtime][rt-threaded], 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");
+//! # }
+//! ```
+//!
+//! In addition, this module also 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 another task 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;
+//! # }
+//! ```
+//!
+//! [`task::spawn_blocking`]: crate::task::spawn_blocking
+//! [`task::block_in_place`]: crate::task::block_in_place
+//! [rt-threaded]: crate::runtime::Runtime::builder::threaded_scheduler
+//! [`task::yield_now`]: crate::task::yield_now()
+//! [`thread::yield_now`]: std::thread::yield_now
cfg_blocking! {
mod blocking;
pub use blocking::spawn_blocking;