diff options
Diffstat (limited to 'src/lib.rs')
-rw-r--r-- | src/lib.rs | 149 |
1 files changed, 149 insertions, 0 deletions
diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..5518515 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,149 @@ +//! Task abstraction for building executors. +//! +//! # What is an executor? +//! +//! An async block creates a future and an async function returns one. But futures don't do +//! anything unless they are awaited inside other async blocks or async functions. So the question +//! arises: who or what awaits the main future that awaits others? +//! +//! One solution is to call [`block_on()`] on the main future, which will block +//! the current thread and keep polling the future until it completes. But sometimes we don't want +//! to block the current thread and would prefer to *spawn* the future to let a background thread +//! block on it instead. +//! +//! This is where executors step in - they create a number of threads (typically equal to the +//! number of CPU cores on the system) that are dedicated to polling spawned futures. Each executor +//! thread keeps polling spawned futures in a loop and only blocks when all spawned futures are +//! either sleeping or running. +//! +//! # What is a task? +//! +//! In order to spawn a future on an executor, one needs to allocate the future on the heap and +//! keep some state alongside it, like whether the future is ready for polling, waiting to be woken +//! up, or completed. This allocation is usually called a *task*. +//! +//! The executor then runs the spawned task by polling its future. If the future is pending on a +//! resource, a [`Waker`] associated with the task will be registered somewhere so that the task +//! can be woken up and run again at a later time. +//! +//! For example, if the future wants to read something from a TCP socket that is not ready yet, the +//! networking system will clone the task's waker and wake it up once the socket becomes ready. +//! +//! # Task construction +//! +//! A task is constructed with [`Task::create()`]: +//! +//! ``` +//! # #![feature(async_await)] +//! let future = async { 1 + 2 }; +//! let schedule = |task| unimplemented!(); +//! +//! let (task, handle) = async_task::spawn(future, schedule, ()); +//! ``` +//! +//! The first argument to the constructor, `()` in this example, is an arbitrary piece of data +//! called a *tag*. This can be a task identifier, a task name, task-local storage, or something +//! of similar nature. +//! +//! The second argument is the future that gets polled when the task is run. +//! +//! The third argument is the schedule function, which is called every time when the task gets +//! woken up. This function should push the received task into some kind of queue of runnable +//! tasks. +//! +//! The constructor returns a runnable [`Task`] and a [`JoinHandle`] that can await the result of +//! the future. +//! +//! # Task scheduling +//! +//! TODO +//! +//! # Join handles +//! +//! TODO +//! +//! # Cancellation +//! +//! TODO +//! +//! # Performance +//! +//! TODO: explain single allocation, etc. +//! +//! Task [construction] incurs a single allocation only. The [`Task`] can then be run and its +//! result awaited through the [`JoinHandle`]. When woken, the task gets automatically rescheduled. +//! It's also possible to cancel the task so that it stops running and can't be awaited anymore. +//! +//! [construction]: struct.Task.html#method.create +//! [`JoinHandle`]: struct.JoinHandle.html +//! [`Task`]: struct.Task.html +//! [`Future`]: https://doc.rust-lang.org/nightly/std/future/trait.Future.html +//! [`Waker`]: https://doc.rust-lang.org/nightly/std/task/struct.Waker.html +//! [`block_on()`]: https://docs.rs/futures-preview/*/futures/executor/fn.block_on.html +//! +//! # Examples +//! +//! A simple single-threaded executor: +//! +//! ``` +//! # #![feature(async_await)] +//! use std::future::Future; +//! use std::panic::catch_unwind; +//! use std::thread; +//! +//! use async_task::{JoinHandle, Task}; +//! use crossbeam::channel::{unbounded, Sender}; +//! use futures::executor; +//! use lazy_static::lazy_static; +//! +//! /// Spawns a future on the executor. +//! fn spawn<F, R>(future: F) -> JoinHandle<R, ()> +//! where +//! F: Future<Output = R> + Send + 'static, +//! R: Send + 'static, +//! { +//! lazy_static! { +//! // A channel that holds scheduled tasks. +//! static ref QUEUE: Sender<Task<()>> = { +//! let (sender, receiver) = unbounded::<Task<()>>(); +//! +//! // Start the executor thread. +//! thread::spawn(|| { +//! for task in receiver { +//! // Ignore panics for simplicity. +//! let _ignore_panic = catch_unwind(|| task.run()); +//! } +//! }); +//! +//! sender +//! }; +//! } +//! +//! // Create a task that is scheduled by sending itself into the channel. +//! let schedule = |t| QUEUE.send(t).unwrap(); +//! let (task, handle) = async_task::spawn(future, schedule, ()); +//! +//! // Schedule the task by sending it into the channel. +//! task.schedule(); +//! +//! handle +//! } +//! +//! // Spawn a future and await its result. +//! let handle = spawn(async { +//! println!("Hello, world!"); +//! }); +//! executor::block_on(handle); +//! ``` + +#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)] + +mod header; +mod join_handle; +mod raw; +mod state; +mod task; +mod utils; + +pub use crate::join_handle::JoinHandle; +pub use crate::task::{spawn, Task}; |