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 | //! #### Cancellation |
116 | //! |
117 | //! Spawned tasks may be cancelled using the [`JoinHandle::abort`] or |
118 | //! [`AbortHandle::abort`] methods. When one of these methods are called, the |
119 | //! task is signalled to shut down next time it yields at an `.await` point. If |
120 | //! the task is already idle, then it will be shut down as soon as possible |
121 | //! without running again before being shut down. Additionally, shutting down a |
122 | //! Tokio runtime (e.g. by returning from `#[tokio::main]`) immediately cancels |
123 | //! all tasks on it. |
124 | //! |
125 | //! When tasks are shut down, it will stop running at whichever `.await` it has |
126 | //! yielded at. All local variables are destroyed by running their detructor. |
127 | //! Once shutdown has completed, awaiting the [`JoinHandle`] will fail with a |
128 | //! [cancelled error](crate::task::JoinError::is_cancelled). |
129 | //! |
130 | //! Note that aborting a task does not guarantee that it fails with a cancelled |
131 | //! error, since it may complete normally first. For example, if the task does |
132 | //! not yield to the runtime at any point between the call to `abort` and the |
133 | //! end of the task, then the [`JoinHandle`] will instead report that the task |
134 | //! exited normally. |
135 | //! |
136 | //! Be aware that calls to [`JoinHandle::abort`] just schedule the task for |
137 | //! cancellation, and will return before the cancellation has completed. To wait |
138 | //! for cancellation to complete, wait for the task to finish by awaiting the |
139 | //! [`JoinHandle`]. Similarly, the [`JoinHandle::is_finished`] method does not |
140 | //! return `true` until the cancellation has finished. |
141 | //! |
142 | //! Calling [`JoinHandle::abort`] multiple times has the same effect as calling |
143 | //! it once. |
144 | //! |
145 | //! Tokio also provides an [`AbortHandle`], which is like the [`JoinHandle`], |
146 | //! except that it does not provide a mechanism to wait for the task to finish. |
147 | //! Each task can only have one [`JoinHandle`], but it can have more than one |
148 | //! [`AbortHandle`]. |
149 | //! |
150 | //! [`JoinHandle::abort`]: crate::task::JoinHandle::abort |
151 | //! [`AbortHandle::abort`]: crate::task::AbortHandle::abort |
152 | //! [`AbortHandle`]: crate::task::AbortHandle |
153 | //! [`JoinHandle::is_finished`]: crate::task::JoinHandle::is_finished |
154 | //! |
155 | //! ### Blocking and Yielding |
156 | //! |
157 | //! As we discussed above, code running in asynchronous tasks should not perform |
158 | //! operations that can block. A blocking operation performed in a task running |
159 | //! on a thread that is also running other tasks would block the entire thread, |
160 | //! preventing other tasks from running. |
161 | //! |
162 | //! Instead, Tokio provides two APIs for running blocking operations in an |
163 | //! asynchronous context: [`task::spawn_blocking`] and [`task::block_in_place`]. |
164 | //! |
165 | //! Be aware that if you call a non-async method from async code, that non-async |
166 | //! method is still inside the asynchronous context, so you should also avoid |
167 | //! blocking operations there. This includes destructors of objects destroyed in |
168 | //! async code. |
169 | //! |
170 | //! #### spawn_blocking |
171 | //! |
172 | //! The `task::spawn_blocking` function is similar to the `task::spawn` function |
173 | //! discussed in the previous section, but rather than spawning an |
174 | //! _non-blocking_ future on the Tokio runtime, it instead spawns a |
175 | //! _blocking_ function on a dedicated thread pool for blocking tasks. For |
176 | //! example: |
177 | //! |
178 | //! ``` |
179 | //! use tokio::task; |
180 | //! |
181 | //! # async fn docs() { |
182 | //! task::spawn_blocking(|| { |
183 | //! // do some compute-heavy work or call synchronous code |
184 | //! }); |
185 | //! # } |
186 | //! ``` |
187 | //! |
188 | //! Just like `task::spawn`, `task::spawn_blocking` returns a `JoinHandle` |
189 | //! which we can use to await the result of the blocking operation: |
190 | //! |
191 | //! ```rust |
192 | //! # use tokio::task; |
193 | //! # async fn docs() -> Result<(), Box<dyn std::error::Error>>{ |
194 | //! let join = task::spawn_blocking(|| { |
195 | //! // do some compute-heavy work or call synchronous code |
196 | //! "blocking completed" |
197 | //! }); |
198 | //! |
199 | //! let result = join.await?; |
200 | //! assert_eq!(result, "blocking completed" ); |
201 | //! # Ok(()) |
202 | //! # } |
203 | //! ``` |
204 | //! |
205 | //! #### block_in_place |
206 | //! |
207 | //! When using the [multi-threaded runtime][rt-multi-thread], the [`task::block_in_place`] |
208 | //! function is also available. Like `task::spawn_blocking`, this function |
209 | //! allows running a blocking operation from an asynchronous context. Unlike |
210 | //! `spawn_blocking`, however, `block_in_place` works by transitioning the |
211 | //! _current_ worker thread to a blocking thread, moving other tasks running on |
212 | //! that thread to another worker thread. This can improve performance by avoiding |
213 | //! context switches. |
214 | //! |
215 | //! For example: |
216 | //! |
217 | //! ``` |
218 | //! use tokio::task; |
219 | //! |
220 | //! # async fn docs() { |
221 | //! let result = task::block_in_place(|| { |
222 | //! // do some compute-heavy work or call synchronous code |
223 | //! "blocking completed" |
224 | //! }); |
225 | //! |
226 | //! assert_eq!(result, "blocking completed" ); |
227 | //! # } |
228 | //! ``` |
229 | //! |
230 | //! #### yield_now |
231 | //! |
232 | //! In addition, this module provides a [`task::yield_now`] async function |
233 | //! that is analogous to the standard library's [`thread::yield_now`]. Calling |
234 | //! and `await`ing this function will cause the current task to yield to the |
235 | //! Tokio runtime's scheduler, allowing other tasks to be |
236 | //! scheduled. Eventually, the yielding task will be polled again, allowing it |
237 | //! to execute. For example: |
238 | //! |
239 | //! ```rust |
240 | //! use tokio::task; |
241 | //! |
242 | //! # #[tokio::main] async fn main() { |
243 | //! async { |
244 | //! task::spawn(async { |
245 | //! // ... |
246 | //! println!("spawned task done!" ) |
247 | //! }); |
248 | //! |
249 | //! // Yield, allowing the newly-spawned task to execute first. |
250 | //! task::yield_now().await; |
251 | //! println!("main task done!" ); |
252 | //! } |
253 | //! # .await; |
254 | //! # } |
255 | //! ``` |
256 | //! |
257 | //! ### Cooperative scheduling |
258 | //! |
259 | //! A single call to [`poll`] on a top-level task may potentially do a lot of |
260 | //! work before it returns `Poll::Pending`. If a task runs for a long period of |
261 | //! time without yielding back to the executor, it can starve other tasks |
262 | //! waiting on that executor to execute them, or drive underlying resources. |
263 | //! Since Rust does not have a runtime, it is difficult to forcibly preempt a |
264 | //! long-running task. Instead, this module provides an opt-in mechanism for |
265 | //! futures to collaborate with the executor to avoid starvation. |
266 | //! |
267 | //! Consider a future like this one: |
268 | //! |
269 | //! ``` |
270 | //! # use tokio_stream::{Stream, StreamExt}; |
271 | //! async fn drop_all<I: Stream + Unpin>(mut input: I) { |
272 | //! while let Some(_) = input.next().await {} |
273 | //! } |
274 | //! ``` |
275 | //! |
276 | //! It may look harmless, but consider what happens under heavy load if the |
277 | //! input stream is _always_ ready. If we spawn `drop_all`, the task will never |
278 | //! yield, and will starve other tasks and resources on the same executor. |
279 | //! |
280 | //! To account for this, Tokio has explicit yield points in a number of library |
281 | //! functions, which force tasks to return to the executor periodically. |
282 | //! |
283 | //! |
284 | //! #### unconstrained |
285 | //! |
286 | //! If necessary, [`task::unconstrained`] lets you opt a future out of of Tokio's cooperative |
287 | //! scheduling. When a future is wrapped with `unconstrained`, it will never be forced to yield to |
288 | //! Tokio. For example: |
289 | //! |
290 | //! ``` |
291 | //! # #[tokio::main] |
292 | //! # async fn main() { |
293 | //! use tokio::{task, sync::mpsc}; |
294 | //! |
295 | //! let fut = async { |
296 | //! let (tx, mut rx) = mpsc::unbounded_channel(); |
297 | //! |
298 | //! for i in 0..1000 { |
299 | //! let _ = tx.send(()); |
300 | //! // This will always be ready. If coop was in effect, this code would be forced to yield |
301 | //! // periodically. However, if left unconstrained, then this code will never yield. |
302 | //! rx.recv().await; |
303 | //! } |
304 | //! }; |
305 | //! |
306 | //! task::unconstrained(fut).await; |
307 | //! # } |
308 | //! ``` |
309 | //! |
310 | //! [`task::spawn_blocking`]: crate::task::spawn_blocking |
311 | //! [`task::block_in_place`]: crate::task::block_in_place |
312 | //! [rt-multi-thread]: ../runtime/index.html#threaded-scheduler |
313 | //! [`task::yield_now`]: crate::task::yield_now() |
314 | //! [`thread::yield_now`]: std::thread::yield_now |
315 | //! [`task::unconstrained`]: crate::task::unconstrained() |
316 | //! [`poll`]: method@std::future::Future::poll |
317 | |
318 | cfg_rt! { |
319 | pub use crate::runtime::task::{JoinError, JoinHandle}; |
320 | |
321 | cfg_not_wasi! { |
322 | mod blocking; |
323 | pub use blocking::spawn_blocking; |
324 | } |
325 | |
326 | mod spawn; |
327 | pub use spawn::spawn; |
328 | |
329 | cfg_rt_multi_thread! { |
330 | pub use blocking::block_in_place; |
331 | } |
332 | |
333 | mod yield_now; |
334 | pub use yield_now::yield_now; |
335 | |
336 | cfg_unstable! { |
337 | mod consume_budget; |
338 | pub use consume_budget::consume_budget; |
339 | } |
340 | |
341 | mod local; |
342 | pub use local::{spawn_local, LocalSet, LocalEnterGuard}; |
343 | |
344 | mod task_local; |
345 | pub use task_local::LocalKey; |
346 | |
347 | mod unconstrained; |
348 | pub use unconstrained::{unconstrained, Unconstrained}; |
349 | |
350 | #[doc (inline)] |
351 | pub use join_set::JoinSet; |
352 | pub use crate::runtime::task::AbortHandle; |
353 | |
354 | // Uses #[cfg(...)] instead of macro since the macro adds docsrs annotations. |
355 | #[cfg (not(tokio_unstable))] |
356 | mod join_set; |
357 | #[cfg (tokio_unstable)] |
358 | pub mod join_set; |
359 | |
360 | cfg_unstable! { |
361 | pub use crate::runtime::task::{Id, id, try_id}; |
362 | } |
363 | |
364 | cfg_trace! { |
365 | mod builder; |
366 | pub use builder::Builder; |
367 | } |
368 | |
369 | /// Task-related futures. |
370 | pub mod futures { |
371 | pub use super::task_local::TaskLocalFuture; |
372 | } |
373 | } |
374 | |