1 //! Asynchronous green-threads. 2 //! 3 //! ## What are Tasks? 4 //! 5 //! A _task_ is a light weight, non-blocking unit of execution. A task is similar 6 //! to an OS thread, but rather than being managed by the OS scheduler, they are 7 //! managed by the [Tokio runtime][rt]. Another name for this general pattern is 8 //! [green threads]. If you are familiar with [Go's goroutines], [Kotlin's 9 //! coroutines], or [Erlang's processes], you can think of Tokio's tasks as 10 //! something similar. 11 //! 12 //! Key points about tasks include: 13 //! 14 //! * Tasks are **light weight**. Because tasks are scheduled by the Tokio 15 //! runtime rather than the operating system, creating new tasks or switching 16 //! between tasks does not require a context switch and has fairly low 17 //! overhead. Creating, running, and destroying large numbers of tasks is 18 //! quite cheap, especially compared to OS threads. 19 //! 20 //! * Tasks are scheduled **cooperatively**. Most operating systems implement 21 //! _preemptive multitasking_. This is a scheduling technique where the 22 //! operating system allows each thread to run for a period of time, and then 23 //! _preempts_ it, temporarily pausing that thread and switching to another. 24 //! Tasks, on the other hand, implement _cooperative multitasking_. In 25 //! cooperative multitasking, a task is allowed to run until it _yields_, 26 //! indicating to the Tokio runtime's scheduler that it cannot currently 27 //! continue executing. When a task yields, the Tokio runtime switches to 28 //! executing the next task. 29 //! 30 //! * Tasks are **non-blocking**. Typically, when an OS thread performs I/O or 31 //! must synchronize with another thread, it _blocks_, allowing the OS to 32 //! schedule another thread. When a task cannot continue executing, it must 33 //! yield instead, allowing the Tokio runtime to schedule another task. Tasks 34 //! should generally not perform system calls or other operations that could 35 //! block a thread, as this would prevent other tasks running on the same 36 //! thread from executing as well. Instead, this module provides APIs for 37 //! running blocking operations in an asynchronous context. 38 //! 39 //! [rt]: crate::runtime 40 //! [green threads]: https://en.wikipedia.org/wiki/Green_threads 41 //! [Go's goroutines]: https://tour.golang.org/concurrency/1 42 //! [Kotlin's coroutines]: https://kotlinlang.org/docs/reference/coroutines-overview.html 43 //! [Erlang's processes]: http://erlang.org/doc/getting_started/conc_prog.html#processes 44 //! 45 //! ## Working with Tasks 46 //! 47 //! This module provides the following APIs for working with tasks: 48 //! 49 //! ### Spawning 50 //! 51 //! Perhaps the most important function in this module is [`task::spawn`]. This 52 //! function can be thought of as an async equivalent to the standard library's 53 //! [`thread::spawn`][`std::thread::spawn`]. It takes an `async` block or other 54 //! [future], and creates a new task to run that work concurrently: 55 //! 56 //! ``` 57 //! use tokio::task; 58 //! 59 //! # async fn doc() { 60 //! task::spawn(async { 61 //! // perform some work here... 62 //! }); 63 //! # } 64 //! ``` 65 //! 66 //! Like [`std::thread::spawn`], `task::spawn` returns a [`JoinHandle`] struct. 67 //! A `JoinHandle` is itself a future which may be used to await the output of 68 //! the spawned task. For example: 69 //! 70 //! ``` 71 //! use tokio::task; 72 //! 73 //! # #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { 74 //! let join = task::spawn(async { 75 //! // ... 76 //! "hello world!" 77 //! }); 78 //! 79 //! // ... 80 //! 81 //! // Await the result of the spawned task. 82 //! let result = join.await?; 83 //! assert_eq!(result, "hello world!"); 84 //! # Ok(()) 85 //! # } 86 //! ``` 87 //! 88 //! Again, like `std::thread`'s [`JoinHandle` type][thread_join], if the spawned 89 //! task panics, awaiting its `JoinHandle` will return a [`JoinError`]`. For 90 //! example: 91 //! 92 //! ``` 93 //! use tokio::task; 94 //! 95 //! # #[tokio::main] async fn main() { 96 //! let join = task::spawn(async { 97 //! panic!("something bad happened!") 98 //! }); 99 //! 100 //! // The returned result indicates that the task failed. 101 //! assert!(join.await.is_err()); 102 //! # } 103 //! ``` 104 //! 105 //! `spawn`, `JoinHandle`, and `JoinError` are present when the "rt" 106 //! feature flag is enabled. 107 //! 108 //! [`task::spawn`]: crate::task::spawn() 109 //! [future]: std::future::Future 110 //! [`std::thread::spawn`]: std::thread::spawn 111 //! [`JoinHandle`]: crate::task::JoinHandle 112 //! [thread_join]: std::thread::JoinHandle 113 //! [`JoinError`]: crate::task::JoinError 114 //! 115 //! ### Blocking and Yielding 116 //! 117 //! As we discussed above, code running in asynchronous tasks should not perform 118 //! operations that can block. A blocking operation performed in a task running 119 //! on a thread that is also running other tasks would block the entire thread, 120 //! preventing other tasks from running. 121 //! 122 //! Instead, Tokio provides two APIs for running blocking operations in an 123 //! asynchronous context: [`task::spawn_blocking`] and [`task::block_in_place`]. 124 //! 125 //! #### spawn_blocking 126 //! 127 //! The `task::spawn_blocking` function is similar to the `task::spawn` function 128 //! discussed in the previous section, but rather than spawning an 129 //! _non-blocking_ future on the Tokio runtime, it instead spawns a 130 //! _blocking_ function on a dedicated thread pool for blocking tasks. For 131 //! example: 132 //! 133 //! ``` 134 //! use tokio::task; 135 //! 136 //! # async fn docs() { 137 //! task::spawn_blocking(|| { 138 //! // do some compute-heavy work or call synchronous code 139 //! }); 140 //! # } 141 //! ``` 142 //! 143 //! Just like `task::spawn`, `task::spawn_blocking` returns a `JoinHandle` 144 //! which we can use to await the result of the blocking operation: 145 //! 146 //! ```rust 147 //! # use tokio::task; 148 //! # async fn docs() -> Result<(), Box<dyn std::error::Error>>{ 149 //! let join = task::spawn_blocking(|| { 150 //! // do some compute-heavy work or call synchronous code 151 //! "blocking completed" 152 //! }); 153 //! 154 //! let result = join.await?; 155 //! assert_eq!(result, "blocking completed"); 156 //! # Ok(()) 157 //! # } 158 //! ``` 159 //! 160 //! #### block_in_place 161 //! 162 //! When using the [multi-threaded runtime][rt-multi-thread], the [`task::block_in_place`] 163 //! function is also available. Like `task::spawn_blocking`, this function 164 //! allows running a blocking operation from an asynchronous context. Unlike 165 //! `spawn_blocking`, however, `block_in_place` works by transitioning the 166 //! _current_ worker thread to a blocking thread, moving other tasks running on 167 //! that thread to another worker thread. This can improve performance by avoiding 168 //! context switches. 169 //! 170 //! For example: 171 //! 172 //! ``` 173 //! use tokio::task; 174 //! 175 //! # async fn docs() { 176 //! let result = task::block_in_place(|| { 177 //! // do some compute-heavy work or call synchronous code 178 //! "blocking completed" 179 //! }); 180 //! 181 //! assert_eq!(result, "blocking completed"); 182 //! # } 183 //! ``` 184 //! 185 //! #### yield_now 186 //! 187 //! In addition, this module provides a [`task::yield_now`] async function 188 //! that is analogous to the standard library's [`thread::yield_now`]. Calling 189 //! and `await`ing this function will cause the current task to yield to the 190 //! Tokio runtime's scheduler, allowing other tasks to be 191 //! scheduled. Eventually, the yielding task will be polled again, allowing it 192 //! to execute. For example: 193 //! 194 //! ```rust 195 //! use tokio::task; 196 //! 197 //! # #[tokio::main] async fn main() { 198 //! async { 199 //! task::spawn(async { 200 //! // ... 201 //! println!("spawned task done!") 202 //! }); 203 //! 204 //! // Yield, allowing the newly-spawned task to execute first. 205 //! task::yield_now().await; 206 //! println!("main task done!"); 207 //! } 208 //! # .await; 209 //! # } 210 //! ``` 211 //! 212 //! ### Cooperative scheduling 213 //! 214 //! A single call to [`poll`] on a top-level task may potentially do a lot of 215 //! work before it returns `Poll::Pending`. If a task runs for a long period of 216 //! time without yielding back to the executor, it can starve other tasks 217 //! waiting on that executor to execute them, or drive underlying resources. 218 //! Since Rust does not have a runtime, it is difficult to forcibly preempt a 219 //! long-running task. Instead, this module provides an opt-in mechanism for 220 //! futures to collaborate with the executor to avoid starvation. 221 //! 222 //! Consider a future like this one: 223 //! 224 //! ``` 225 //! # use tokio_stream::{Stream, StreamExt}; 226 //! async fn drop_all<I: Stream + Unpin>(mut input: I) { 227 //! while let Some(_) = input.next().await {} 228 //! } 229 //! ``` 230 //! 231 //! It may look harmless, but consider what happens under heavy load if the 232 //! input stream is _always_ ready. If we spawn `drop_all`, the task will never 233 //! yield, and will starve other tasks and resources on the same executor. 234 //! 235 //! To account for this, Tokio has explicit yield points in a number of library 236 //! functions, which force tasks to return to the executor periodically. 237 //! 238 //! 239 //! #### unconstrained 240 //! 241 //! If necessary, [`task::unconstrained`] lets you opt out a future of Tokio's cooperative 242 //! scheduling. When a future is wrapped with `unconstrained`, it will never be forced to yield to 243 //! Tokio. For example: 244 //! 245 //! ``` 246 //! # #[tokio::main] 247 //! # async fn main() { 248 //! use tokio::{task, sync::mpsc}; 249 //! 250 //! let fut = async { 251 //! let (tx, mut rx) = mpsc::unbounded_channel(); 252 //! 253 //! for i in 0..1000 { 254 //! let _ = tx.send(()); 255 //! // This will always be ready. If coop was in effect, this code would be forced to yield 256 //! // periodically. However, if left unconstrained, then this code will never yield. 257 //! rx.recv().await; 258 //! } 259 //! }; 260 //! 261 //! task::unconstrained(fut).await; 262 //! # } 263 //! ``` 264 //! 265 //! [`task::spawn_blocking`]: crate::task::spawn_blocking 266 //! [`task::block_in_place`]: crate::task::block_in_place 267 //! [rt-multi-thread]: ../runtime/index.html#threaded-scheduler 268 //! [`task::yield_now`]: crate::task::yield_now() 269 //! [`thread::yield_now`]: std::thread::yield_now 270 //! [`task::unconstrained`]: crate::task::unconstrained() 271 //! [`poll`]: method@std::future::Future::poll 272 273 cfg_rt! { 274 pub use crate::runtime::task::{JoinError, JoinHandle}; 275 276 mod blocking; 277 pub use blocking::spawn_blocking; 278 279 mod spawn; 280 pub use spawn::spawn; 281 282 cfg_rt_multi_thread! { 283 pub use blocking::block_in_place; 284 } 285 286 mod yield_now; 287 pub use yield_now::yield_now; 288 289 mod local; 290 pub use local::{spawn_local, LocalSet}; 291 292 mod task_local; 293 pub use task_local::LocalKey; 294 295 mod unconstrained; 296 pub use unconstrained::{unconstrained, Unconstrained}; 297 } 298