Futures need to be polled to completion. This is the job of an executor.
Some futures may need to wait for events from the kernel, for example to know when there might be data ready to read from a network channel.
A reactor handles this (by using mio
or poll
) to register for events
from the kernel and know when things might be able to progress.
std::future
has the core futures functionality needed to implement async/await
syntax.
std::future::Future
is a trait representing an asyncronous computation.
The core method of future, poll
, attemps to resolve the future into a final
value. This method does not block if the value is not ready. Instead, the
current task is scheduled to be woken up when it's possible to make further
progress by polling again.
The poll
function returns:
Poll::Pending
if the future is not ready yet;Poll::Ready(val)
if the future has finished.When the future has finished, clients should not poll
again.
When using a future, you generally won't poll
directy, but instead .await
the value.
std::task::Waker
is a handle for waking up a task by notifying its executor
that it is ready to be run (implements Clone
, Send
and Sync
).
The context passed to the Future::poll
method can provide a Waker
for waking
up the current task.
When future is not ready yet, poll
returns Poll::Pending
and stores a clone
of the Waker
copied from the current Context
. This Waker
is then woken-up
once the future can make progress.
Futures alone are inert; they must be actively poll
ed to make progress,
meaning that each time the current task is woken up, it should actively re-poll
pending futures that it still has interest in.
Runtimes are not included in the standard library and is typically provided by an external crate.
futures-rs
is a crate which adds utility and abstraction over futures:
FutureExt, TryFutureExt, Stream, StreamExt, TryStreamExt, Sink, SinkExt.
Tokio
brings an async runtime and some additional utility to interact with
environment in async way: IO, time, unix signals, sync primitives.
Tokio is built over futures-rs
.
Has an executor and a reactor bundled within it.
Futures that rely on the tokio::io/fs
need to be run inside the context of a
tokio runtime (which makes the tokio reactor available to them and allows
spawning).
Brings an API very similar to standard lib, but for async programming. Somehow like futures-rs and tokio merged together.
Uses a different executor than tokio.
A small and fast async runtime implemented around async-executor
.
Doesn't come with a reactor itself.
Has a compatibility layer to interact withh tokio-based libraries.
Why Waker
is not implemented as a trait object?
If you make Waker
a trait, then you must use it as a trait object, because if
the Future trait is generic over the kind of waker, then it becomes non-object
safe, making Box<dyn Future>
impossible.
If you make it an ordinary trait object, then what should clone return? It can't return Self, as that makes the Waker trait non-object safe, so it must return a trait object. However returning a trait object is not possible without allocating, and we want async/await to be feasible on embedded systems without allocations.
Proudly self-hosted on a cheap Raspberry Pi