Stjepan Glavina | 1479e86 | 2019-08-12 20:18:51 +0200 | [diff] [blame^] | 1 | //! Task abstraction for building executors. |
| 2 | //! |
| 3 | //! # What is an executor? |
| 4 | //! |
| 5 | //! An async block creates a future and an async function returns one. But futures don't do |
| 6 | //! anything unless they are awaited inside other async blocks or async functions. So the question |
| 7 | //! arises: who or what awaits the main future that awaits others? |
| 8 | //! |
| 9 | //! One solution is to call [`block_on()`] on the main future, which will block |
| 10 | //! the current thread and keep polling the future until it completes. But sometimes we don't want |
| 11 | //! to block the current thread and would prefer to *spawn* the future to let a background thread |
| 12 | //! block on it instead. |
| 13 | //! |
| 14 | //! This is where executors step in - they create a number of threads (typically equal to the |
| 15 | //! number of CPU cores on the system) that are dedicated to polling spawned futures. Each executor |
| 16 | //! thread keeps polling spawned futures in a loop and only blocks when all spawned futures are |
| 17 | //! either sleeping or running. |
| 18 | //! |
| 19 | //! # What is a task? |
| 20 | //! |
| 21 | //! In order to spawn a future on an executor, one needs to allocate the future on the heap and |
| 22 | //! keep some state alongside it, like whether the future is ready for polling, waiting to be woken |
| 23 | //! up, or completed. This allocation is usually called a *task*. |
| 24 | //! |
| 25 | //! The executor then runs the spawned task by polling its future. If the future is pending on a |
| 26 | //! resource, a [`Waker`] associated with the task will be registered somewhere so that the task |
| 27 | //! can be woken up and run again at a later time. |
| 28 | //! |
| 29 | //! For example, if the future wants to read something from a TCP socket that is not ready yet, the |
| 30 | //! networking system will clone the task's waker and wake it up once the socket becomes ready. |
| 31 | //! |
| 32 | //! # Task construction |
| 33 | //! |
| 34 | //! A task is constructed with [`Task::create()`]: |
| 35 | //! |
| 36 | //! ``` |
| 37 | //! # #![feature(async_await)] |
| 38 | //! let future = async { 1 + 2 }; |
| 39 | //! let schedule = |task| unimplemented!(); |
| 40 | //! |
| 41 | //! let (task, handle) = async_task::spawn(future, schedule, ()); |
| 42 | //! ``` |
| 43 | //! |
| 44 | //! The first argument to the constructor, `()` in this example, is an arbitrary piece of data |
| 45 | //! called a *tag*. This can be a task identifier, a task name, task-local storage, or something |
| 46 | //! of similar nature. |
| 47 | //! |
| 48 | //! The second argument is the future that gets polled when the task is run. |
| 49 | //! |
| 50 | //! The third argument is the schedule function, which is called every time when the task gets |
| 51 | //! woken up. This function should push the received task into some kind of queue of runnable |
| 52 | //! tasks. |
| 53 | //! |
| 54 | //! The constructor returns a runnable [`Task`] and a [`JoinHandle`] that can await the result of |
| 55 | //! the future. |
| 56 | //! |
| 57 | //! # Task scheduling |
| 58 | //! |
| 59 | //! TODO |
| 60 | //! |
| 61 | //! # Join handles |
| 62 | //! |
| 63 | //! TODO |
| 64 | //! |
| 65 | //! # Cancellation |
| 66 | //! |
| 67 | //! TODO |
| 68 | //! |
| 69 | //! # Performance |
| 70 | //! |
| 71 | //! TODO: explain single allocation, etc. |
| 72 | //! |
| 73 | //! Task [construction] incurs a single allocation only. The [`Task`] can then be run and its |
| 74 | //! result awaited through the [`JoinHandle`]. When woken, the task gets automatically rescheduled. |
| 75 | //! It's also possible to cancel the task so that it stops running and can't be awaited anymore. |
| 76 | //! |
| 77 | //! [construction]: struct.Task.html#method.create |
| 78 | //! [`JoinHandle`]: struct.JoinHandle.html |
| 79 | //! [`Task`]: struct.Task.html |
| 80 | //! [`Future`]: https://doc.rust-lang.org/nightly/std/future/trait.Future.html |
| 81 | //! [`Waker`]: https://doc.rust-lang.org/nightly/std/task/struct.Waker.html |
| 82 | //! [`block_on()`]: https://docs.rs/futures-preview/*/futures/executor/fn.block_on.html |
| 83 | //! |
| 84 | //! # Examples |
| 85 | //! |
| 86 | //! A simple single-threaded executor: |
| 87 | //! |
| 88 | //! ``` |
| 89 | //! # #![feature(async_await)] |
| 90 | //! use std::future::Future; |
| 91 | //! use std::panic::catch_unwind; |
| 92 | //! use std::thread; |
| 93 | //! |
| 94 | //! use async_task::{JoinHandle, Task}; |
| 95 | //! use crossbeam::channel::{unbounded, Sender}; |
| 96 | //! use futures::executor; |
| 97 | //! use lazy_static::lazy_static; |
| 98 | //! |
| 99 | //! /// Spawns a future on the executor. |
| 100 | //! fn spawn<F, R>(future: F) -> JoinHandle<R, ()> |
| 101 | //! where |
| 102 | //! F: Future<Output = R> + Send + 'static, |
| 103 | //! R: Send + 'static, |
| 104 | //! { |
| 105 | //! lazy_static! { |
| 106 | //! // A channel that holds scheduled tasks. |
| 107 | //! static ref QUEUE: Sender<Task<()>> = { |
| 108 | //! let (sender, receiver) = unbounded::<Task<()>>(); |
| 109 | //! |
| 110 | //! // Start the executor thread. |
| 111 | //! thread::spawn(|| { |
| 112 | //! for task in receiver { |
| 113 | //! // Ignore panics for simplicity. |
| 114 | //! let _ignore_panic = catch_unwind(|| task.run()); |
| 115 | //! } |
| 116 | //! }); |
| 117 | //! |
| 118 | //! sender |
| 119 | //! }; |
| 120 | //! } |
| 121 | //! |
| 122 | //! // Create a task that is scheduled by sending itself into the channel. |
| 123 | //! let schedule = |t| QUEUE.send(t).unwrap(); |
| 124 | //! let (task, handle) = async_task::spawn(future, schedule, ()); |
| 125 | //! |
| 126 | //! // Schedule the task by sending it into the channel. |
| 127 | //! task.schedule(); |
| 128 | //! |
| 129 | //! handle |
| 130 | //! } |
| 131 | //! |
| 132 | //! // Spawn a future and await its result. |
| 133 | //! let handle = spawn(async { |
| 134 | //! println!("Hello, world!"); |
| 135 | //! }); |
| 136 | //! executor::block_on(handle); |
| 137 | //! ``` |
| 138 | |
| 139 | #![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] |
| 140 | |
| 141 | mod header; |
| 142 | mod join_handle; |
| 143 | mod raw; |
| 144 | mod state; |
| 145 | mod task; |
| 146 | mod utils; |
| 147 | |
| 148 | pub use crate::join_handle::JoinHandle; |
| 149 | pub use crate::task::{spawn, Task}; |