aboutsummaryrefslogtreecommitdiff
path: root/src/lib.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib.rs')
-rw-r--r--src/lib.rs149
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};