Skip to content

Commit 7a8962b

Browse files
author
Stjepan Glavina
committed
Rewrite docs
1 parent 1c5032d commit 7a8962b

File tree

4 files changed

+107
-415
lines changed

4 files changed

+107
-415
lines changed

src/join_handle.rs

Lines changed: 8 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -12,36 +12,10 @@ use crate::utils::abort_on_panic;
1212

1313
/// A handle that awaits the result of a task.
1414
///
15-
/// If the task has completed with `value`, the handle returns it as `Some(value)`. If the task was
16-
/// cancelled or has panicked, the handle returns `None`. Otherwise, the handle has to wait until
17-
/// the task completes, panics, or gets cancelled.
15+
/// This type is a future that resolves to an `Option<R>` where:
1816
///
19-
/// # Examples
20-
///
21-
/// ```
22-
/// #![feature(async_await)]
23-
///
24-
/// use crossbeam::channel;
25-
/// use futures::executor;
26-
///
27-
/// // The future inside the task.
28-
/// let future = async { 1 + 2 };
29-
///
30-
/// // If the task gets woken, it will be sent into this channel.
31-
/// let (s, r) = channel::unbounded();
32-
/// let schedule = move |task| s.send(task).unwrap();
33-
///
34-
/// // Create a task with the future and the schedule function.
35-
/// let (task, handle) = async_task::spawn(future, schedule, ());
36-
///
37-
/// // Run the task. In this example, it will complete after a single run.
38-
/// task.run();
39-
/// assert!(r.is_empty());
40-
///
41-
/// // Await the result of the task.
42-
/// let result = executor::block_on(handle);
43-
/// assert_eq!(result, Some(3));
44-
/// ```
17+
/// * `None` indicates the task has panicked or was cancelled
18+
/// * `Some(res)` indicates the task has completed with `res`
4519
pub struct JoinHandle<R, T> {
4620
/// A raw task pointer.
4721
pub(crate) raw_task: NonNull<()>,
@@ -58,39 +32,9 @@ impl<R, T> Unpin for JoinHandle<R, T> {}
5832
impl<R, T> JoinHandle<R, T> {
5933
/// Cancels the task.
6034
///
61-
/// When cancelled, the task won't be scheduled again even if a [`Waker`] wakes it. An attempt
62-
/// to run it won't do anything. And if it's completed, awaiting its result evaluates to
63-
/// `None`.
64-
///
65-
/// [`Waker`]: https://doc.rust-lang.org/std/task/struct.Waker.html
66-
///
67-
/// # Examples
68-
///
69-
/// ```
70-
/// # #![feature(async_await)]
71-
/// use crossbeam::channel;
72-
/// use futures::executor;
73-
///
74-
/// // The future inside the task.
75-
/// let future = async { 1 + 2 };
35+
/// If the task has already completed, calling this method will have no effect.
7636
///
77-
/// // If the task gets woken, it will be sent into this channel.
78-
/// let (s, r) = channel::unbounded();
79-
/// let schedule = move |task| s.send(task).unwrap();
80-
///
81-
/// // Create a task with the future and the schedule function.
82-
/// let (task, handle) = async_task::spawn(future, schedule, ());
83-
///
84-
/// // Cancel the task.
85-
/// handle.cancel();
86-
///
87-
/// // Running a cancelled task does nothing.
88-
/// task.run();
89-
///
90-
/// // Await the result of the task.
91-
/// let result = executor::block_on(handle);
92-
/// assert_eq!(result, None);
93-
/// ```
37+
/// When a task is cancelled, its future cannot be polled again and will be dropped instead.
9438
pub fn cancel(&self) {
9539
let ptr = self.raw_task.as_ptr();
9640
let header = ptr as *const Header;
@@ -139,26 +83,6 @@ impl<R, T> JoinHandle<R, T> {
13983
}
14084

14185
/// Returns a reference to the tag stored inside the task.
142-
///
143-
/// # Examples
144-
///
145-
/// ```
146-
/// # #![feature(async_await)]
147-
/// use crossbeam::channel;
148-
///
149-
/// // The future inside the task.
150-
/// let future = async { 1 + 2 };
151-
///
152-
/// // If the task gets woken, it will be sent into this channel.
153-
/// let (s, r) = channel::unbounded();
154-
/// let schedule = move |task| s.send(task).unwrap();
155-
///
156-
/// // Create a task with the future and the schedule function.
157-
/// let (task, handle) = async_task::spawn(future, schedule, "a simple task");
158-
///
159-
/// // Access the tag.
160-
/// assert_eq!(*handle.tag(), "a simple task");
161-
/// ```
16286
pub fn tag(&self) -> &T {
16387
let offset = Header::offset_tag::<T>();
16488
let ptr = self.raw_task.as_ptr();
@@ -210,9 +134,9 @@ impl<R, T> Drop for JoinHandle<R, T> {
210134
Err(s) => state = s,
211135
}
212136
} else {
213-
// If this is the last reference to task and it's not closed, then close
214-
// it and schedule one more time so that its future gets dropped by the
215-
// executor.
137+
// If this is the last reference to the task and it's not closed, then
138+
// close it and schedule one more time so that its future gets dropped by
139+
// the executor.
216140
let new = if state & (!(REFERENCE - 1) | CLOSED) == 0 {
217141
SCHEDULED | CLOSED | REFERENCE
218142
} else {

src/lib.rs

Lines changed: 76 additions & 108 deletions
Original file line numberDiff line numberDiff line change
@@ -1,142 +1,110 @@
11
//! Task abstraction for building executors.
22
//!
3-
//! # What is an executor?
3+
//! To spawn a future onto an executor, we first need to allocate it on the heap and keep some
4+
//! state alongside it. The state indicates whether the future is ready for polling, waiting to be
5+
//! woken up, or completed. Such a future is called a *task*.
46
//!
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?
7+
//! This crate helps with task allocation and polling its future to completion.
88
//!
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.
9+
//! # Spawning
1310
//!
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.
11+
//! All executors have some kind of queue that holds runnable tasks:
1812
//!
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
13+
//! ```
14+
//! # #![feature(async_await)]
15+
//! #
16+
//! let (sender, receiver) = crossbeam::channel::unbounded();
17+
//! #
18+
//! # // A future that will get spawned.
19+
//! # let future = async { 1 + 2 };
20+
//! #
21+
//! # // A function that schedules the task when it gets woken up.
22+
//! # let schedule = move |task| sender.send(task).unwrap();
23+
//! #
24+
//! # // Construct a task.
25+
//! # let (task, handle) = async_task::spawn(future, schedule, ());
26+
//! ```
3327
//!
34-
//! A task is constructed with [`Task::create()`]:
28+
//! A task is constructed using the [`spawn`] function:
3529
//!
3630
//! ```
3731
//! # #![feature(async_await)]
32+
//! #
33+
//! # let (sender, receiver) = crossbeam::channel::unbounded();
34+
//! #
35+
//! // A future that will be spawned.
3836
//! let future = async { 1 + 2 };
39-
//! let schedule = |task| unimplemented!();
4037
//!
41-
//! let (task, handle) = async_task::spawn(future, schedule, ());
42-
//! ```
38+
//! // A function that schedules the task when it gets woken up.
39+
//! let schedule = move |task| sender.send(task).unwrap();
4340
//!
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.
41+
//! // Construct a task.
42+
//! let (task, handle) = async_task::spawn(future, schedule, ());
4743
//!
48-
//! The second argument is the future that gets polled when the task is run.
44+
//! // Push the task into the queue by invoking its schedule function.
45+
//! task.schedule();
46+
//! ```
4947
//!
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.
48+
//! The last argument to the [`spawn`] function is a *tag*, an arbitrary piece of data associated
49+
//! with the task. In most executors, this is typically a task identifier or task-local storage.
5350
//!
54-
//! The constructor returns a runnable [`Task`] and a [`JoinHandle`] that can await the result of
55-
//! the future.
51+
//! The function returns a runnable [`Task`] and a [`JoinHandle`] that can await the result.
5652
//!
57-
//! # Task scheduling
53+
//! # Execution
5854
//!
59-
//! TODO
55+
//! Task executors have some kind of main loop that drives tasks to completion. That means taking
56+
//! runnable tasks out of the queue and running each one in order:
6057
//!
61-
//! # Join handles
58+
//! ```no_run
59+
//! # #![feature(async_await)]
60+
//! #
61+
//! # let (sender, receiver) = crossbeam::channel::unbounded();
62+
//! #
63+
//! # // A future that will get spawned.
64+
//! # let future = async { 1 + 2 };
65+
//! #
66+
//! # // A function that schedules the task when it gets woken up.
67+
//! # let schedule = move |task| sender.send(task).unwrap();
68+
//! #
69+
//! # // Construct a task.
70+
//! # let (task, handle) = async_task::spawn(future, schedule, ());
71+
//! #
72+
//! # // Push the task into the queue by invoking its schedule function.
73+
//! # task.schedule();
74+
//! #
75+
//! for task in receiver {
76+
//! task.run();
77+
//! }
78+
//! ```
6279
//!
63-
//! TODO
80+
//! When a task is run, its future gets polled. If polling does not complete the task, that means
81+
//! it's waiting for another future and needs to go to sleep. When woken up, its schedule function
82+
//! will be invoked, pushing it back into the queue so that it can be run again.
6483
//!
6584
//! # Cancellation
6685
//!
67-
//! TODO
86+
//! Both [`Task`] and [`JoinHandle`] have a method that cancels the task. When cancelled, the
87+
//! task's future will not be polled again and will get dropped instead.
88+
//!
89+
//! If cancelled by the [`Task`] instance, the task is destroyed immediately. If cancelled by the
90+
//! [`JoinHandle`] instance, it will be scheduled one more time and the next attempt to run it will
91+
//! simply destroy it.
6892
//!
6993
//! # Performance
7094
//!
71-
//! TODO: explain single allocation, etc.
95+
//! Task construction incurs a single allocation only that holds its state, the schedule function,
96+
//! and the future or the result of the future if completed.
7297
//!
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.
98+
//! The layout of a task is equivalent to 4 words followed by the schedule function, and then by a
99+
//! union of the future and its output.
76100
//!
77-
//! [construction]: struct.Task.html#method.create
78-
//! [`JoinHandle`]: struct.JoinHandle.html
101+
//! [`spawn`]: fn.spawn.html
79102
//! [`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-
//! ```
103+
//! [`JoinHandle`]: struct.JoinHandle.html
138104
139105
#![warn(missing_docs, missing_debug_implementations, rust_2018_idioms)]
106+
#![doc(test(attr(deny(rust_2018_idioms, warnings))))]
107+
#![doc(test(attr(allow(unused_extern_crates, unused_variables))))]
140108

141109
mod header;
142110
mod join_handle;

src/raw.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -353,7 +353,7 @@ where
353353
let new = (*raw.header).state.fetch_sub(REFERENCE, Ordering::AcqRel) - REFERENCE;
354354

355355
// If this was the last reference to the task and the `JoinHandle` has been dropped as
356-
// well, then destroy task.
356+
// well, then destroy the task.
357357
if new & !(REFERENCE - 1) == 0 && new & HANDLE == 0 {
358358
Self::destroy(ptr);
359359
}

0 commit comments

Comments
 (0)