mirror of https://github.com/rust-lang/async-book
Move draft from wg-net
This commit is contained in:
parent
29c28ccb7e
commit
d598899e95
2
LICENSE
2
LICENSE
|
@ -1,6 +1,6 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2018 The Rust Programming Language
|
||||
Copyright (c) 2018 Aaron Turon
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
# Table of Contents
|
||||
|
||||
- [Getting Started](getting_started/chapter.md)
|
||||
- [What This Book Covers](getting_started/chapter.md#what-this-book-covers)
|
||||
- [Why Async?](getting_started/why_async.md)
|
||||
- [The State of Asynchronous Rust](getting_started/state_of_async_rust.md)
|
||||
- [`async`/`await!` Primer](getting_started/async_await_primer.md)
|
||||
- [Applied: HTTP Server](getting_started/http_server_example.md)
|
||||
- [Under the Hood: Executing `Future`s and Tasks](execution/chapter.md)
|
||||
- [The `Future` Trait](execution/future.md)
|
||||
- [Task Wakeups with `LocalWaker` and `Waker`](execution/wakeups.md)
|
||||
- [Applied: Build a Timer](execution/wakeups.md)
|
||||
- [Applied: Build an Executor](execution/executor.md)
|
||||
- [Executors and System IO](execution/io.md)
|
||||
- [`async`/`await`]()
|
||||
- [What and Why]()
|
||||
- [`async` Blocks, Closures, and Functions]()
|
||||
- [Applied: XXX]()
|
||||
- [Pinning](pinning/chapter.md)
|
||||
- [Practical Usage](pinning/chapter.md#how-to-use-pinning)
|
||||
- [Streams](streams/chapter.md)
|
||||
- [Patterns: Iteration and Concurrency]()
|
||||
- [Executing Multiple Futures at a Time]()
|
||||
- [`select!` and `join!`]()
|
||||
- [Spawning]()
|
||||
- [Cancellation and Timeouts]()
|
||||
- [`FuturesUnordered`]()
|
||||
- [I/O]()
|
||||
- [`AsyncRead` and `AsyncWrite`]()
|
||||
- [Asynchronous Design Patterns: Solutions and Suggestions]()
|
||||
- [Modeling Servers and the Request/Response Pattern]()
|
||||
- [Managing Shared State]()
|
||||
- [The Ecosystem: Tokio and More]()
|
||||
- Lots, lots more?...
|
|
@ -0,0 +1,154 @@
|
|||
# `async`/`await!`
|
||||
|
||||
In [the first chapter], we took a brief look at `async`/`await!` and used
|
||||
it to build a simple server. This chapter will discuss `async`/`await!` in
|
||||
greater detail, explaining how it works and how `async` code differs from
|
||||
traditional Rust programs.
|
||||
|
||||
`async`/`await!` are special pieces of Rust syntax that make it possible to
|
||||
yield control of the current thread rather than blocking, allowing other
|
||||
code to make progress while waiting on an operation to complete.
|
||||
|
||||
There are three main ways to use `async`: `async fn`, `async` blocks, and
|
||||
`async` closures. Each returns a value that implements the `Future` trait:
|
||||
|
||||
```rust
|
||||
// `foo()` returns a type that implements `Future<Output = u8>`.
|
||||
// `await!(foo())` will result in a value of type `u8`.
|
||||
async fn foo() -> u8 { 5 }
|
||||
|
||||
fn bar() -> impl Future<Output = u8> {
|
||||
// This `async` block results in a type that implements
|
||||
// `Future<Output = u8>`.
|
||||
async {
|
||||
let x: u8 = await!(foo());
|
||||
x + 5
|
||||
}
|
||||
}
|
||||
|
||||
fn baz() -> impl Future<Output = u8> {
|
||||
// This `async` closure, when called, returns a type that
|
||||
// implements `Future<Output = u8>`
|
||||
let closure = async |x: u8| {
|
||||
await!(bar()) + x
|
||||
};
|
||||
closure(5)
|
||||
}
|
||||
```
|
||||
|
||||
As we saw in the first chapter, `async` bodies and other futures are lazy:
|
||||
they do nothing until they are run. The most common way to run a `Future`
|
||||
is to `await!` it. When `await!` is called on a `Future`, it will attempt
|
||||
to run it to completion. If the `Future` is blocked, it will yield control
|
||||
of the current thread. When more progress can be made, the `Future` will be picked
|
||||
up by the executor and will resume running, allowing the `await!` to resolve.
|
||||
|
||||
## `async` Lifetimes
|
||||
|
||||
Unlike traditional functions, `async fn`s which take references or other
|
||||
non-`'static` arguments return a `Future` which is bounded by the lifetime of
|
||||
the arguments:
|
||||
|
||||
```rust
|
||||
// This function:
|
||||
async fn foo(x: &u8) -> u8 { *x }
|
||||
|
||||
// Is equivalent ot this function:
|
||||
fn foo<'a>(x: &'a u8) -> impl Future<Output = ()> + 'a {
|
||||
async { *x }
|
||||
}
|
||||
```
|
||||
|
||||
This means that the future returned from an `async fn` must be `await!`ed
|
||||
while its non-`'static` arguments are still valid. In the common
|
||||
case of `await!`ing the future immediately after calling the function
|
||||
(like `await!(foo(&x))`) this is not an issue. However, if storing the future
|
||||
or sending it over to another task or thread, this may be an issue.
|
||||
|
||||
One common workaround for turning an `async fn` with references-as-arguments
|
||||
into a `'static` future is to bundle the arguments with the call to the
|
||||
`async fn` inside an `async` block:
|
||||
|
||||
```rust
|
||||
async fn foo(x: &u8) -> u8 { *x }
|
||||
|
||||
fn bad() -> impl Future<Output = ()> {
|
||||
let x = 5;
|
||||
foo(&x) // ERROR: `x` does not live long enough
|
||||
}
|
||||
|
||||
fn good() -> impl Future<Output = ()> {
|
||||
async {
|
||||
let x = 5;
|
||||
await!(foo(&x))
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
By moving the argument into the `async` block, we extend its lifetime to match
|
||||
that of the `Future` returned from the call to `foo`.
|
||||
|
||||
## `async move`
|
||||
|
||||
`async` blocks and closures allow the `move` keyword, much like normal
|
||||
closures. An `async move` block will take ownership of the variables it
|
||||
references, allowing it to outlive the current scope, but giving up the ability
|
||||
to share those variables with other code:
|
||||
|
||||
```rust
|
||||
/// `async` block:
|
||||
///
|
||||
/// Multiple different `async` blocks can access the same local variable
|
||||
/// so long as they're executed within the variable's scope.
|
||||
async fn foo() {
|
||||
let my_string = "foo".to_string();
|
||||
|
||||
let future_one = async {
|
||||
...
|
||||
println!("{}", my_string);
|
||||
};
|
||||
|
||||
let future_two = async {
|
||||
...
|
||||
println!("{}", my_string);
|
||||
};
|
||||
|
||||
// Run both futures to completion, printing "foo" twice
|
||||
let ((), ()) = join!(future_one, future_two);
|
||||
}
|
||||
|
||||
/// `async move` block:
|
||||
///
|
||||
/// Only one `async` block can access captured variables, since they are
|
||||
/// moved into the `Future` generated by the `async` block. However,
|
||||
/// this allows the `Future` to outlive the original scope of the variable:
|
||||
fn foo() -> impl Future<Output = ()> {
|
||||
let my_string = "foo".to_string();
|
||||
async move {
|
||||
...
|
||||
println!("{}", my_string);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## `await!`ing on a Multithreaded Executor
|
||||
|
||||
Note that, when using a multithreaded `Future` executor, a `Future` may move
|
||||
between threads, so any variables used in `async` bodies must be able to travel
|
||||
between threads, as any `await!` can potentially result in a switch to a new
|
||||
thread.
|
||||
|
||||
This means that it is not safe to use `Rc`, `&RefCell` or any other types
|
||||
that don't implement the `Send` trait, including references to types that don't
|
||||
implement the `Sync` trait.
|
||||
|
||||
(Caveat: it is possible to use these types so long as they aren't in scope
|
||||
during a call to `await!`.)
|
||||
|
||||
Similarly, it isn't a good idea to hold a traditional non-futures-aware lock
|
||||
across an `await!`, as it can cause the threadpool to lock up: one task could
|
||||
take out a lock, `await!` and yield to the executor, allowing another task to
|
||||
attempt to take the lock and cause a deadlock. To avoid this, use the `Mutex`
|
||||
in `futures::lock` rather than the one from `std::sync`.
|
||||
|
||||
[the first chapter]: TODO ../getting_started/async_await_primer.md
|
|
@ -0,0 +1,13 @@
|
|||
# Under the Hood: Executing `Future`s and Tasks
|
||||
|
||||
In this section, we'll cover the underlying structure of how `Future`s and
|
||||
asynchronous tasks are scheduled. If you're only interested in learning
|
||||
how to write higher-level code that uses existing `Future` types and aren't
|
||||
interested in the details of how `Future` types work, you can skip ahead to
|
||||
the `async`/`await` chapter. However, several of the topics discussed in this
|
||||
chapter are useful for understanding how `async`/`await` code works,
|
||||
understanding the runtime and performance properties of `async`/`await` code,
|
||||
and building new asynchronous primitives. If you decide to skip this section
|
||||
now, you may want to bookmark it to revisit in the future.
|
||||
|
||||
Now, with that out of the, way, let's talk about the `Future` trait.
|
|
@ -0,0 +1,183 @@
|
|||
# Applied: Build an Executor
|
||||
|
||||
`Future`s are lazy and must be actively driven to completion in order to do
|
||||
anything. A common way to drive a future to completion is to `await!` it inside
|
||||
an `async` function, but that just pushes the problem one level up: who will
|
||||
run the futures returned from the top-level `async` functions? The answer is
|
||||
that we need a `Future` executor.
|
||||
|
||||
`Future` executors take a set of top-level `Future`s and run them to completion
|
||||
by calling `poll` whenever the `Future` can make progress. Typically, an
|
||||
executor will `poll` a future once to start off. When `Future`s indicate that
|
||||
they are ready to make progress by calling `wake()`, they are placed back
|
||||
onto a queue and `poll` is called again, repeating until the `Future` has
|
||||
completed.
|
||||
|
||||
In this section, we'll write our own simple executor capable of running a large
|
||||
number of top-level futures to completion concurrently.
|
||||
|
||||
For this one, we're going to have to include the `futures` crate in order to
|
||||
get the `FutureObj` type, which is a dynamically-dispatched `Future`, similar
|
||||
to `Box<dyn Future<Output = T>>`. `Cargo.toml` should look something like this:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "xyz"
|
||||
version = "0.1.0"
|
||||
authors = ["XYZ Author"]
|
||||
edition = "2018"
|
||||
|
||||
[dependencies]
|
||||
futures-preview = "0.3.0-alpha.9"
|
||||
```
|
||||
|
||||
Next, we need the following imports at the top of `src/main.rs`:
|
||||
|
||||
```rust
|
||||
#![feature(arbitrary_self_types, async_await, await_macro, futures_api, pin)]
|
||||
|
||||
use {
|
||||
futures::future::FutureObj,
|
||||
std::{
|
||||
future::Future,
|
||||
pin::Pin,
|
||||
sync::{Arc, Mutex},
|
||||
sync::mpsc::{sync_channel, SyncSender, Receiver},
|
||||
task::{
|
||||
local_waker_from_nonlocal,
|
||||
Poll, Wake,
|
||||
},
|
||||
},
|
||||
};
|
||||
```
|
||||
|
||||
Our executor will work by sending tasks to run over a channel. The executor
|
||||
will pull events off of the channel and run them. When a task is ready to
|
||||
do more work (is awoken), it can schedule itself to be polled again by
|
||||
putting itself back onto the channel.
|
||||
|
||||
In this design, the executor itself just needs the receiving end of the task
|
||||
channel. The user will get a sending end so that they can spawn new futures.
|
||||
Tasks themselves are just futures that can reschedule themselves, so we'll
|
||||
store them as a future paired with a sender that the task can use to requeue
|
||||
itself.
|
||||
|
||||
```rust
|
||||
/// Task executor that receives tasks off of a channel and runs them.
|
||||
struct Executor {
|
||||
ready_queue: Receiver<Arc<Task>>,
|
||||
}
|
||||
|
||||
/// `Spawner` spawns new futures onto the task channel.
|
||||
#[derive(Clone)]
|
||||
struct Spawner {
|
||||
task_sender: SyncSender<Arc<Task>>,
|
||||
}
|
||||
|
||||
/// A future that can reschedule itself to be polled using a channel.
|
||||
struct Task {
|
||||
// In-progress future that should be pushed to completion
|
||||
//
|
||||
// The `Mutex` is not necessary for correctness, since we only have
|
||||
// one thread executing tasks at once. However, `rustc` isn't smart
|
||||
// enough to know that `future` is only mutated from one thread,
|
||||
// so we use it in order to provide safety. A production executor would
|
||||
// not need this, and could use `UnsafeCell` instead.
|
||||
future: Mutex<Option<FutureObj<'static, ()>>>,
|
||||
|
||||
// Handle to spawn tasks onto the task queue
|
||||
task_sender: SyncSender<Arc<Task>>,
|
||||
}
|
||||
|
||||
fn new_executor_and_spawner() -> (Executor, Spawner) {
|
||||
// Maximum number of tasks to allow queueing in the channel at once.
|
||||
// This is just to make `sync_channel` happy, and wouldn't be present in
|
||||
// a real executor.
|
||||
const MAX_QUEUED_TASKS: usize = 10_000;
|
||||
let (task_sender, ready_queue) = sync_channel(MAX_QUEUED_TASKS);
|
||||
(Executor { ready_queue }, Spawner { task_sender})
|
||||
}
|
||||
```
|
||||
|
||||
Let's also add a method to spawner to make it easy to spawn new futures.
|
||||
This method will take a future type, box it and put it in a FutureObj,
|
||||
and create a new `Arc<Task>` with it inside which can be enqueued onto the
|
||||
executor.
|
||||
|
||||
```rust
|
||||
impl Spawner {
|
||||
fn spawn(&self, future: impl Future<Output = ()> + 'static + Send) {
|
||||
let future_obj = FutureObj::new(Box::new(future));
|
||||
let task = Arc::new(Task {
|
||||
future: Mutex::new(Some(future_obj)),
|
||||
task_sender: self.task_sender.clone(),
|
||||
});
|
||||
self.task_sender.send(task).expect("too many tasks queued");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In order poll futures, we'll also need to create a `LocalWaker` to provide to
|
||||
poll. As discussed in the [task wakeups section], `LocalWaker`s are responsible
|
||||
for scheduling a task to be polled again once `wake` is called. Remember that
|
||||
`LocalWaker`s tell the executor exactly which task has become ready, allowing
|
||||
them to poll just the futures that are ready to make progress. The easiest way
|
||||
to create a new `LocalWaker` is by implementing the `Wake` trait and then using
|
||||
the `local_waker_from_nonlocal` or `local_waker` functions to turn a `Arc<T: Wake>`
|
||||
into a `LocalWaker`. Let's implement `Wake` for our tasks to allow them to be
|
||||
turned into `LocalWaker`s and awoken:
|
||||
|
||||
```rust
|
||||
impl Wake for Task {
|
||||
fn wake(arc_self: &Arc<Self>) {
|
||||
// Implement `wake` by sending this task back onto the task channel
|
||||
// so that it will be polled again by the executor.
|
||||
let cloned = arc_self.clone();
|
||||
arc_self.task_sender.send(cloned).expect("too many tasks queued");
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
When a `LocalWaker` is created from an `Arc<Task>`, calling `wake()` on it will
|
||||
cause a copy of the `Arc` to be sent onto the task channel. Our executor then
|
||||
needs to pick up the task and poll it. Let's implement that:
|
||||
|
||||
```rust
|
||||
impl Executor {
|
||||
fn run(&self) {
|
||||
while let Ok(task) = self.ready_queue.recv() {
|
||||
let mut future_slot = task.future.lock().unwrap();
|
||||
// Take the future, and if it has not yet completed (is still Some),
|
||||
// poll it in an attempt to complete it.
|
||||
if let Some(mut future) = future_slot.take() {
|
||||
// Create a `LocalWaker` from the task itself
|
||||
let lw = local_waker_from_nonlocal(task.clone());
|
||||
if let Poll::Pending = Pin::new(&mut future).poll(&lw) {
|
||||
// We're not done processing the future, so put it
|
||||
// back in its task to be run again in the future.
|
||||
*future_slot = Some(future);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Congratulations! We now have a working futures executor. We can even use it
|
||||
to run `async/await!` code and custom futures, such as the `TimerFuture` we
|
||||
wrote earlier:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let (executor, spawner) = new_executor_and_spawner();
|
||||
spawner.spawn(async {
|
||||
println!("howdy!");
|
||||
// Wait for our timer future to complete after two seconds.
|
||||
await!(TimerFuture::new(Duration::new(2, 0)));
|
||||
println!("done!");
|
||||
});
|
||||
executor.run();
|
||||
}
|
||||
```
|
||||
|
||||
[task wakeups section]: TODO
|
|
@ -0,0 +1,189 @@
|
|||
# The `Future` Trait
|
||||
|
||||
The `Future` trait is at the center of asynchronous programming in Rust.
|
||||
A `Future` is an asynchronous computation that can produce a value
|
||||
(although that value may be empty, e.g. `()`). A *simplified* version of
|
||||
the future trait might look something like this:
|
||||
|
||||
```rust
|
||||
trait SimpleFuture {
|
||||
type Output;
|
||||
fn poll(&mut self, wake: fn()) -> Poll<Self::Output>;
|
||||
}
|
||||
|
||||
enum Poll<T> {
|
||||
Ready(T),
|
||||
Pending,
|
||||
}
|
||||
```
|
||||
|
||||
Futures can be advanced by calling the `poll` function, which will drive the
|
||||
future as far towards completion as possible. If the future completes, it
|
||||
returns `Poll::Ready(result)`. If the future is not able to complete yet, it
|
||||
returns `Poll::Pending` and arranges for the `wake()` function to be called
|
||||
when the `Future` is ready to make more progress. When `wake()` is called, the
|
||||
executor driving the `Future` will call `poll` again so that the `Future` can
|
||||
make more progress.
|
||||
|
||||
Without `wake()`, the executor would have no way of knowing when a particular
|
||||
future could make progress, and would have to be constantly polling every
|
||||
future. With `wake()`, the executor knows exactly which futures are ready to
|
||||
be `poll`ed.
|
||||
|
||||
For example, consider the case where we want to read from a socket that may
|
||||
or may not have data available already. If there is data, we can read it
|
||||
in and return `Poll::Ready(data)`, but if no data is ready, our future is
|
||||
blocked and can no longer make progress. When no data is available, we
|
||||
must register `wake` to be called when data becomes ready on the socket,
|
||||
which will tell the executor that our future is ready to make progress.
|
||||
A simple `SocketRead` future might look something like this:
|
||||
|
||||
```rust
|
||||
struct SocketRead<'a> {
|
||||
socket: &'a Socket,
|
||||
}
|
||||
|
||||
impl SimpleFuture for SocketRead<'_> {
|
||||
type Output = Vec<u8>;
|
||||
|
||||
fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
|
||||
if self.socket.has_data_to_read() {
|
||||
// The socket has data-- read it into a buffer and return it.
|
||||
Poll::Ready(self.socket.read_buf())
|
||||
} else {
|
||||
// The socket does not yet have data.
|
||||
//
|
||||
// Arrange for `wake` to be called once data is available.
|
||||
// When data becomes available, `wake` will be called, and the
|
||||
// user of this `Future` will know to call `poll` again and
|
||||
// receive data.
|
||||
self.socket.set_readable_callback(wake);
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This model of `Future`s allows for composing together multiple asynchronous
|
||||
operations without needing intermediate allocations. Running multiple futures
|
||||
at once or chaining futures together can be implemented via allocation-free
|
||||
state machines, like this:
|
||||
|
||||
```rust
|
||||
/// A SimpleFuture that runs two other futures to completion concurrently.
|
||||
///
|
||||
/// Concurrency is achieved via the fact that calls to `poll` each future
|
||||
/// may be interleaved, allowing each future to advance itself at its own pace.
|
||||
struct Join2 {
|
||||
// Each field may contain a future that should be run to completion.
|
||||
// If the future has already completed, the field is set to `None`.
|
||||
a: Option<FutureA>,
|
||||
b: Option<FutureB>,
|
||||
}
|
||||
|
||||
impl SimpleFuture for Join2 {
|
||||
type Output = ();
|
||||
fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
|
||||
// Attempt to complete future `a`.
|
||||
let finished_a = match &mut self.a {
|
||||
Some(a) => {
|
||||
match a.poll(wake) {
|
||||
Poll::Ready(()) => true,
|
||||
Poll::Pending => false,
|
||||
}
|
||||
}
|
||||
None => true,
|
||||
};
|
||||
if finished_a { self.a.take() }
|
||||
|
||||
// Attempt to complete future `b`.
|
||||
let finished_b = match &mut self.b {
|
||||
Some(b) => {
|
||||
match b.poll(wake) {
|
||||
Poll::Ready(()) => true,
|
||||
Poll::Pending => false,
|
||||
}
|
||||
}
|
||||
None => true,
|
||||
};
|
||||
if finished_b { self.b.take() }
|
||||
|
||||
if finished_a && finished_b {
|
||||
// Both futures have completed-- we can return successfully
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
// One or both futures still have work to do, and will call
|
||||
// `wake()` when progress can be made.
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This shows how multiple futures can be run simultaneously without needing
|
||||
separate allocations, allowing for more efficient asynchronous programs.
|
||||
Similarly, multiple sequential futures can be run one after another, like this:
|
||||
|
||||
```rust
|
||||
/// A SimpleFuture that runs two futures to completion, one after another.
|
||||
//
|
||||
// Note: for the purposes of this simple example, `AndThenFut` assumes both
|
||||
// the first and second futures are available at creation-time. The real
|
||||
// `AndThen` combinator allows creating the second future based on the output
|
||||
// of the first future, like `get_breakfast.and_then(|food| eat(food))`.
|
||||
enum AndThenFut {
|
||||
first: Option<FutureA>,
|
||||
second: FutureB,
|
||||
}
|
||||
|
||||
impl SimpleFuture for AndThenFut {
|
||||
type Output = ();
|
||||
fn poll(&mut self, wake: fn()) -> Poll<Self::Output> {
|
||||
if let Some(first) = &mut self.first {
|
||||
match first.poll(wake) {
|
||||
// We've completed the first future-- remove it and start on
|
||||
// the second!
|
||||
Poll::Ready(()) => self.first.take(),
|
||||
// We couldn't yet complete the first future.
|
||||
Poll::Pending => return Poll::Pending,
|
||||
}
|
||||
}
|
||||
// Now that the first future is done, attempt to complete the second.
|
||||
second.poll(wake)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
These examples show how the `Future` trait can be used to express asynchronous
|
||||
control flow without requiring multiple allocated objects and deeply nested
|
||||
callbacks. With the basic control-flow out of the way, let's talk about the
|
||||
real `Future` trait and how it is different.
|
||||
|
||||
```rust
|
||||
trait Future {
|
||||
type Output;
|
||||
fn poll(
|
||||
// note the change from `&mut self` to `Pin<&mut Self>`
|
||||
self: Pin<&mut Self>,
|
||||
lw: &LocalWaker, // note the change from `wake: fn()`
|
||||
) -> Poll<Self::Output>;
|
||||
}
|
||||
```
|
||||
|
||||
The first change you'll notice is that our `self` type is no longer `&mut self`,
|
||||
but has changed to `Pin<&mut Self>`. We'll talk more about pinning in [a later
|
||||
section][pinning], but for now know that it allows us to create futures that
|
||||
are immovable. Immovable objects can store pointers between their fields,
|
||||
e.g. `struct MyFut { a: i32, ptr_to_a: *const i32 }`. This feature is necessary
|
||||
in order to enable async/await.
|
||||
|
||||
Secondly, `wake: fn()` has changed to `LocalWaker`. In `SimpleFuture`, we used
|
||||
a call to a function pointer (`fn()`) to tell the future executor that the
|
||||
future in question should be polled. However, since `fn()` is zero-sized, it
|
||||
can't store any data about *which* `Future` called `wake`.
|
||||
In a real-world scenario, a complex application like a web server may have
|
||||
thousands of different connections whose wakeups should all be
|
||||
managed separately. This is where `LocalWaker` and its sibling type `Waker`
|
||||
come in.
|
||||
|
||||
[pinning]: TODO
|
|
@ -0,0 +1,135 @@
|
|||
# Executors and System IO
|
||||
|
||||
In the previous section on [The `Future` Trait], we discussed this example of
|
||||
a future that performed an asynchronous read on a socket:
|
||||
|
||||
```rust
|
||||
struct SocketRead<'a> {
|
||||
socket: &'a Socket,
|
||||
}
|
||||
|
||||
impl SimpleFuture for SocketRead<'_> {
|
||||
type Output = Vec<u8>;
|
||||
|
||||
fn poll(self: Pin<&mut Self>, lw: &LocalWaker) -> Poll<Self::Output> {
|
||||
if self.socket.has_data_to_read() {
|
||||
// The socket has data-- read it into a buffer and return it.
|
||||
Poll::Ready(self.socket.read_buf())
|
||||
} else {
|
||||
// The socket does not yet have data.
|
||||
//
|
||||
// Arrange for `wake` to be called once data is available.
|
||||
// When data becomes available, `wake` will be called, and the
|
||||
// user of this `Future` will know to call `poll` again and
|
||||
// receive data.
|
||||
self.socket.set_readable_callback(lw);
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This future will read available data on a socket, and if no data is available,
|
||||
it will yield to the executor, requesting that its task be awoken when the
|
||||
socket becomes readable again. However, it's not clear from this example how
|
||||
the `Socket` type is implemented, and in particular it isn't obvious how the
|
||||
`set_readable_callback` function works. How can we arrange for `lw.wake()`
|
||||
to be called once the socket becomes readable? One option would be to have
|
||||
a thread that continually checks whether `socket` is readable, calling
|
||||
`wake()` when appropriate. However, this would be quite inefficient, requiring
|
||||
a separate thread for each blocked IO future. This would greatly reduce the
|
||||
efficiency of our async code.
|
||||
|
||||
In practice, this problem is solved through integration with an IO-aware
|
||||
system blocking primitive, such as `epoll` on Linux, `kqueue` on FreeBSD and
|
||||
Mac OS, IOCP on Windows, and `port`s on Fuchsia (all of which are exposed
|
||||
through the cross-platform Rust crate [`mio`]). These primitives all allow
|
||||
a thread to block on multiple asynchronous IO events, returning once one of
|
||||
the events completes. In practice, these APIs usually look something like
|
||||
this:
|
||||
|
||||
```rust
|
||||
struct IoBlocker {
|
||||
...
|
||||
}
|
||||
|
||||
struct Event {
|
||||
// An ID uniquely identifying the event that occurred and was listened for.
|
||||
id: usize,
|
||||
|
||||
// A set of signals to wait for, or which occurred.
|
||||
signals: Signals,
|
||||
}
|
||||
|
||||
impl IoBlocker {
|
||||
/// Create a new collection of asynchronous IO events to block on.
|
||||
fn new() -> Self { ... }
|
||||
|
||||
/// Express an interest in a particular IO event.
|
||||
fn add_io_event_interest(
|
||||
&self,
|
||||
|
||||
/// The object on which the event will occur
|
||||
io_object: &IoObject,
|
||||
|
||||
/// A set of signals that may appear on the `io_object` for
|
||||
/// which an event should be triggered, paried with
|
||||
/// an ID to give to events that result from this interest.
|
||||
event: Event,
|
||||
) { ... }
|
||||
|
||||
/// Block until one of the events occurs.
|
||||
/// This will only trigget
|
||||
fn block(&self) -> Event { ... }
|
||||
}
|
||||
|
||||
let mut io_blocker = IoBlocker::new();
|
||||
io_blocker.add_io_event_interest(
|
||||
&socket_1,
|
||||
Event { id: 1, signals: READABLE },
|
||||
);
|
||||
io_blocker.add_io_event_interest(
|
||||
&socket_2,
|
||||
Event { id: 2, signals: READABLE | WRITABLE },
|
||||
);
|
||||
let event = io_blocker.block();
|
||||
|
||||
// prints e.g. "Socket 1 is now READABLE" if socket one became readable.
|
||||
println!("Socket {:?} is now {:?}", event.id, event.signals);
|
||||
```
|
||||
|
||||
Futures executors can use these primitives to provide asynchronous IO objects
|
||||
such as sockets that can configure callbacks to be run when a particular IO
|
||||
event occurs. In the case of our `SocketRead` example above, the
|
||||
`Socket::set_readable_callback` function might look like the following pseudocode:
|
||||
|
||||
```rust
|
||||
impl Socket {
|
||||
fn set_readable_callback(&self, lw: &LocalWaker) {
|
||||
// `local_executor` is a reference to the local executor.
|
||||
// this could be provided at creation of the socket, but in practice
|
||||
// many executor implementations pass it down through thread local
|
||||
// storage for convenience.
|
||||
let local_executor = self.local_executor;
|
||||
|
||||
// Unique ID for this IO object.
|
||||
let id = self.id;
|
||||
|
||||
// Store the local waker in the executor's map so that it can be called
|
||||
// once the IO event arrives.
|
||||
local_executor.event_map.insert(id, lw.clone());
|
||||
local_executor.add_io_event_interest(
|
||||
&self.socket_file_descriptor,
|
||||
Event { id, signals: READABLE },
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We can now have just one executor thread which can receive and dispatch any
|
||||
IO event to the appropriate `LocalWaker`, which will wake up the corresponding
|
||||
task, allowing the executor to drive more tasks to completion before returning
|
||||
to check for more IO events (and the cycle continues...).
|
||||
|
||||
[The `Future` Trait]: TODO
|
||||
[`mio`]: https://github.com/carllerche/mio
|
|
@ -0,0 +1,142 @@
|
|||
# Task Wakeups with `LocalWaker` and `Waker`
|
||||
|
||||
It's common that futures aren't able to complete the first time they are
|
||||
`poll`ed. When this happens, the future needs to ensure that it is polled
|
||||
again once it is ready to make more progress. This is done with the
|
||||
`LocalWaker` and `Waker` types.
|
||||
|
||||
Each time a future is polled, it is polled as part of a "task". Tasks are
|
||||
the top-level futures that have been submitted to an executor.
|
||||
|
||||
`LocalWaker` and `Waker` each provide a `wake()` method that can be used to
|
||||
tell the executor that their associated task should be awoken. When `wake()` is
|
||||
called, the executor knows that the task associated with the `Waker` is ready to
|
||||
make progress, and its future should be polled again.
|
||||
|
||||
`LocalWaker` and `Waker` also implement `clone()` so that
|
||||
they can be copied around and stored. The difference between the two is
|
||||
thread-safety: `LocalWaker` is `!Send` and `!Sync`, and so cannot be used from
|
||||
threads other than the one it was created from. This allows `LocalWaker`
|
||||
implementations to perform special optimized behavior for the current thread.
|
||||
`Waker`s, on the other hand, are `Send` and `Sync`, and so can be used across
|
||||
multiple threads. A `LocalWaker` can be turned into a thread-safe `Waker` using
|
||||
the `into_waker()` function. This function is free to call-- it doesn't
|
||||
allocate at runtime or anything similar, but calling `wake()` on the resulting
|
||||
`Waker` may be less performant than calling `wake()` on the original
|
||||
`LocalWaker`.
|
||||
|
||||
Let's try implementing a simple timer future using `Waker` and `LocalWaker`.
|
||||
|
||||
## Applied: Build a Timer
|
||||
|
||||
For the sake of the example, we'll just spin up a new thread when the timer
|
||||
is created, sleep for the required time, and then signal the timer future
|
||||
when the time window has elapsed.
|
||||
|
||||
Here are the imports we'll need to get started:
|
||||
|
||||
```rust
|
||||
#![feature(arbitrary_self_types, futures_api, pin)]
|
||||
|
||||
use std::{
|
||||
future::Future,
|
||||
pin::{Pin, Unpin},
|
||||
sync::{Arc, Mutex},
|
||||
task::{LocalWaker, Poll, Waker},
|
||||
thread,
|
||||
time::Duration,
|
||||
};
|
||||
```
|
||||
|
||||
Let's start by defining the future type itself. Our future needs a way for the
|
||||
thread to communicate that the timer has elapsed and the future should complete.
|
||||
We'll use a shared `Arc<Mutex<..>>` value to communicate between the thread and
|
||||
the future.
|
||||
|
||||
```rust
|
||||
struct TimerFuture {
|
||||
shared_state: Arc<Mutex<SharedState>>,
|
||||
}
|
||||
|
||||
/// Shared state between the future and the thread
|
||||
struct SharedState {
|
||||
/// Whether or not the sleep time has elapsed
|
||||
completed: bool,
|
||||
|
||||
/// The waker for the task that `TimerFuture` is running on.
|
||||
/// The thread can use this after setting `completed = true` to tell
|
||||
/// `TimerFuture`'s task to wake up, see that `completed = true`, and
|
||||
/// move forward.
|
||||
waker: Option<Waker>,
|
||||
}
|
||||
|
||||
// Pinning will be covered later-- for now, it's enough to understand that our
|
||||
// `TimerFuture` type doesn't require it, so it is `Unpin`.
|
||||
impl Unpin for TimerFuture {}
|
||||
```
|
||||
|
||||
Now, let's actually write the `Future` implementation!
|
||||
|
||||
```rust
|
||||
impl Future for TimerFuture {
|
||||
type Output = ();
|
||||
fn poll(self: Pin<&mut Self>, lw: &LocalWaker)
|
||||
-> Poll<Self::Output>
|
||||
{
|
||||
// Look at the shared state to see if the timer has already completed.
|
||||
let mut shared_state = self.shared_state.lock().unwrap();
|
||||
if shared_state.completed {
|
||||
Poll::Ready(())
|
||||
} else {
|
||||
// Set waker so that the thread can wake up the current task
|
||||
// when the timer has completed, ensuring that the future is polled
|
||||
// again and sees that `completed = true`.
|
||||
shared_state.waker = Some(lw.clone().into_waker());
|
||||
Poll::Pending
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Pretty simple, right? If the thread has set `shared_state.completed = true`,
|
||||
we're done! Otherwise, we clone the `LocalWaker` for the current task,
|
||||
convert it into a `Waker`, and pass it to `shared_state.waker` so that the
|
||||
thread can wake the task back up.
|
||||
|
||||
Importantly, we have to update the `Waker` every time the future is polled
|
||||
because the future may have moved to a different task with a different
|
||||
`Waker`. This will happen when futures are passed around between tasks after
|
||||
being polled.
|
||||
|
||||
Finally, we need the API to actually construct the timer and start the thread:
|
||||
|
||||
```rust
|
||||
impl TimerFuture {
|
||||
/// Create a new `TimerFuture` which will complete after the provided
|
||||
/// timeout.
|
||||
pub fn new(duration: Duration) -> Self {
|
||||
let shared_state = Arc::new(Mutex::new(SharedState {
|
||||
completed: false,
|
||||
waker: None,
|
||||
}));
|
||||
|
||||
// Spawn the new thread
|
||||
let thread_shared_state = shared_state.clone();
|
||||
thread::spawn(move || {
|
||||
thread::sleep(duration);
|
||||
let mut shared_state = thread_shared_state.lock().unwrap();
|
||||
// Signal that the timer has completed and wake up the last
|
||||
// task on which the future was polled, if one exists.
|
||||
shared_state.completed = true;
|
||||
if let Some(waker) = &shared_state.waker {
|
||||
waker.wake();
|
||||
}
|
||||
});
|
||||
|
||||
TimerFuture { shared_state }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Woot! That's all we need to build a simple timer future. Now, if only we had
|
||||
an executor to run the future on...
|
|
@ -0,0 +1,103 @@
|
|||
# `async`/`await!` Primer
|
||||
|
||||
`async`/`await!` is Rust's built-in tool for writing asynchronous functions
|
||||
that look like synchronous code. `async` transforms a block of code into a
|
||||
state machine that implements a trait called `Future`. Whereas calling a
|
||||
blocking function in a synchronous method would block the whole thread,
|
||||
blocked `Future`s will yield control of the thread, allowing other
|
||||
`Future`s to run.
|
||||
|
||||
To create an asynchronous function, you can use the `async fn` syntax:
|
||||
|
||||
```rust
|
||||
async fn do_something() { ... }
|
||||
```
|
||||
|
||||
The value returned by `async fn` is a `Future` that needs to be run on
|
||||
an executor in order for anything to happen:
|
||||
|
||||
```rust
|
||||
// `block_on` blocks the current thread until the provided future has run to
|
||||
// completion. Other executors provide more complex behavior, like scheudling
|
||||
// multiple futures onto the same thread.
|
||||
use futures::executor::block_on;
|
||||
|
||||
async fn hello_world() {
|
||||
println!("hello, world!");
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let future = hello_world(); // Nothing is printed
|
||||
block_on(future); // `future` is run and "hello, world!" is printed
|
||||
}
|
||||
```
|
||||
|
||||
Inside an `async fn`, you can use `await!` to wait for the completion of
|
||||
another type that implements the `Future` trait, such as the output of
|
||||
another `async fn`. Unlike `block_on`, `await!` doesn't block the current
|
||||
thread, but instead asynchronously waits for the future to complete, allowing
|
||||
other tasks to run if the future is currently unable to make progress.
|
||||
|
||||
For example, imagine that we have three `async fn`: `learn_song`, `sing_song`,
|
||||
and `dance`:
|
||||
|
||||
```rust
|
||||
async fn learn_song() -> Song { ... }
|
||||
async fn sing_song(song: Song) { ... }
|
||||
async fn dance() { ... }
|
||||
```
|
||||
|
||||
One way to do learn, sing, and dance would be to block on each of these
|
||||
individually:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let song = block_on(learn_song());
|
||||
block_on(sing_song(song));
|
||||
block_on(dance);
|
||||
}
|
||||
```
|
||||
|
||||
However, we're not giving the best performance possible this way-- we're
|
||||
only ever doing one thing at once! Clearly we have to learn the song before
|
||||
we can sing it, but it's possible to dance at the same time as learning and
|
||||
singing the song. To do this, we can create two separate `async fn` which
|
||||
can be run concurrently:
|
||||
|
||||
```rust
|
||||
async fn learn_and_sing() {
|
||||
// Wait until the song has been learned before singing it.
|
||||
// We use `await!` here rather than `block_on` to prevent blocking the
|
||||
// thread, which makes it possible to `dance` at the same time.
|
||||
let song = await!(learn_song());
|
||||
await!(sing_song(song));
|
||||
}
|
||||
|
||||
async fn async_main() {
|
||||
let f1 = learn_and_sing();
|
||||
let f2 = dance();
|
||||
|
||||
// `join!` is like `await!` but can wait for multiple futures concurrently.
|
||||
// If we're temporarily blocked in the `learn_and_sing` future, the `dance`
|
||||
// future will take over the current thread. If `dance` becomes blocked,
|
||||
// `learn_and_sing` can take back over. If both futures are blocked, then
|
||||
// `async_main` is blocked and will yield to the executor.
|
||||
join!(f1, f2)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
block_on(async_main());
|
||||
}
|
||||
```
|
||||
|
||||
In this example, learning the song must happen before singing the song, but
|
||||
both learning and singing can happen at the same time as dancing. If we used
|
||||
`block_on(learn_song())` rather than `await!(learn_song())` in `learn_and_sing`,
|
||||
the thread wouldn't be able to do anything else while `learn_song` was running.
|
||||
This would make it impossible to dance at the same time. By `await!`ing
|
||||
the `learn_song` future, we allow other tasks to take over the current thread
|
||||
if `learn_song` is blocked. This makes it possible to run multiple futures
|
||||
to completion concurrently on the same thread.
|
||||
|
||||
Now that you've learned the basics of `async`/`await!`, let's try out an
|
||||
example.
|
|
@ -0,0 +1,25 @@
|
|||
# Getting Started
|
||||
|
||||
Welcome to Asynchronous Programming in Rust! If you're looking to start writing
|
||||
asynchronous Rust code, you've come to the right place. Whether you're building
|
||||
a web server, a database, or an operating system, this book will show you
|
||||
how to use Rust's asynchronous programming tools to get the most out of your
|
||||
hardware.
|
||||
|
||||
## What This Book Covers
|
||||
|
||||
This book aims to be a comprehensive, up-to-date guide to using Rust's async
|
||||
language features and libraries, appropriate for beginners and old hands alike.
|
||||
|
||||
- The early chapters provide an introduction to async programming in general,
|
||||
and to Rust's particular take on it.
|
||||
|
||||
- The middle chapters discuss key utilities and control-flow tools you can use
|
||||
when writing async code, and describe best-practices for structuring libraries
|
||||
and applications to maximize performance and reusability.
|
||||
|
||||
- The last section of the book covers the broader async ecosystem, and provides
|
||||
a number of examples of how to accomplish common tasks.
|
||||
|
||||
With that out of the way, let's explore the exciting world of Asynchronous
|
||||
Programming in Rust!
|
|
@ -0,0 +1,199 @@
|
|||
# Applied: Simple HTTP Server
|
||||
|
||||
Let's use `async`/`await!` to build an echo server!
|
||||
|
||||
To start, run `rustup update nightly` to make sure you've got the latest and
|
||||
greatest copy of Rust-- we're working with bleeding-edge features, so it's
|
||||
essential to stay up-to-date. Once you've done that, run
|
||||
`cargo +nightly new async-await-echo` to create a new project, and open up
|
||||
the resulting `async-await-echo` folder.
|
||||
|
||||
Let's add some dependencies to the `Cargo.toml` file:
|
||||
|
||||
```toml
|
||||
[dependencies]
|
||||
|
||||
# The latest version of the "futures" library, which has lots of utilities
|
||||
# for writing async code. Enable the "tokio-compat" feature to include the
|
||||
# functions for using futures 0.3 and async/await with the Tokio library.
|
||||
futures-preview = { version = "0.3.0-alpha.9", features = ["tokio-compat"] }
|
||||
|
||||
# Hyper is an asynchronous HTTP library. We'll use it to power our HTTP
|
||||
# server and to make HTTP requests.
|
||||
hyper = "0.12.9"
|
||||
|
||||
# Tokio is a runtime for asynchronous I/O applications. Hyper uses
|
||||
# it for the default server runtime. The `tokio` crate also provides an
|
||||
# an `await!` macro similar to the one in `std`, but it supports `await!`ing
|
||||
# both futures 0.1 futures (the kind used by Hyper and Tokio) and
|
||||
# futures 0.3 futures (the kind produced by the new `async`/`await!` language
|
||||
# feature).
|
||||
tokio = { version = "0.1.11", features = ["async-await-preview"] }
|
||||
```
|
||||
|
||||
Now that we've got our dependencies out of the way, let's start writing some
|
||||
code. Open up `src/main.rs` and enable the following features at the top of
|
||||
the file:
|
||||
|
||||
```rust
|
||||
#![feature(async_await, await_macro, futures_api)]
|
||||
```
|
||||
|
||||
- `async_await` adds support for the `async fn` syntax.
|
||||
- `await_macro` adds support for the `await!` macro.
|
||||
- `futures_api` adds support for the nightly `std::future` and `std::task`
|
||||
modules which define the core `Future` trait and dependent types.
|
||||
|
||||
Additionally, we have some imports to add:
|
||||
|
||||
```rust
|
||||
use {
|
||||
hyper::{
|
||||
// Miscellaneous types from Hyper for working with HTTP.
|
||||
Body, Client, Request, Response, Server, Uri,
|
||||
|
||||
// This function turns a closure which returns a future into an
|
||||
// implementation of the the Hyper `Service` trait, which is an
|
||||
// asynchronous function from a generic `Request` to a `Response`.
|
||||
service::service_fn,
|
||||
|
||||
// A function which runs a future to completion using the Hyper runtime.
|
||||
rt::run,
|
||||
},
|
||||
futures::{
|
||||
// `TokioDefaultSpawner` tells futures 0.3 futures how to spawn tasks
|
||||
// onto the Tokio runtime.
|
||||
compat::TokioDefaultSpawner,
|
||||
|
||||
// Extension traits providing additional methods on futures.
|
||||
// `FutureExt` adds methods that work for all futures, whereas
|
||||
// `TryFutureExt` adds methods to futures that return `Result` types.
|
||||
future::{FutureExt, TryFutureExt},
|
||||
},
|
||||
std::net::SocketAddr,
|
||||
|
||||
// This is the redefinition of the await! macro which supports both
|
||||
// futures 0.1 (used by Hyper and Tokio) and futures 0.3 (the new API
|
||||
// exposed by `std::future` and implemented by `async fn` syntax).
|
||||
tokio::await,
|
||||
};
|
||||
```
|
||||
|
||||
Once the imports are out of the way, we can start putting together the
|
||||
boilerplate to allow us to serve requests:
|
||||
|
||||
```rust
|
||||
async fn serve_req(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
async fn run_server(addr: SocketAddr) {
|
||||
println!("Listening on http://{}", addr);
|
||||
|
||||
// Create a server bound on the provided address
|
||||
let serve_future = Server::bind(&addr)
|
||||
// Serve requests using our `async serve_req` function.
|
||||
// `serve` takes a closure which returns a type implementing the
|
||||
// `Service` trait. `service_fn` returns a value implementing the
|
||||
// `Service` trait, and accepts a closure which goes from request
|
||||
// to a future of the response. In order to use our `serve_req`
|
||||
// function with Hyper, we have to box it and put it in a compatability
|
||||
// wrapper to go from a futures 0.3 future (the kind returned by
|
||||
// `async fn`) to a futures 0.1 future (the kind used by Hyper).
|
||||
.serve(|| service_fn(|req|
|
||||
serve_req(req).boxed().compat(TokioDefaultSpawner)
|
||||
));
|
||||
|
||||
// Wait for the server to complete serving or exit with an error.
|
||||
// If an error occurred, print it to stderr.
|
||||
if let Err(e) = await!(serve_future) {
|
||||
eprintln!("server error: {}", e);
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Set the address to run our socket on.
|
||||
let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
|
||||
|
||||
// Call our run_server function, which returns a future.
|
||||
// As with every `async fn`, we need to run that future in order for
|
||||
// `run_server` to do anything. Additionally, since `run_server` is an
|
||||
// `async fn`, we need to convert it from a futures 0.3 future into a
|
||||
// futures 0.1 future.
|
||||
let futures_03_future = run_server(addr);
|
||||
let futures_01_future =
|
||||
futures_03_future.unit_error().boxed().compat(TokioDefaultSpawner);
|
||||
|
||||
// Finally, we can run the future to completion using the `run` function
|
||||
// provided by Hyper.
|
||||
run(futures_01_future);
|
||||
}
|
||||
```
|
||||
|
||||
If you `cargo run` now, you should see the message "Listening on
|
||||
http://127.0.0.1:300" printed on your terminal. If you open that URL in your
|
||||
browser of choice, you'll see "thread ... panicked at 'not yet implemented'."
|
||||
Great! Now we just need to actually handle requests. To start, let's just
|
||||
return a static message:
|
||||
|
||||
```rust
|
||||
async fn serve_req(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
|
||||
// Always return successfully with a response containing a body with
|
||||
// a friendly greeting ;)
|
||||
Ok(Response::new(Body::from("hello, world!")))
|
||||
}
|
||||
```
|
||||
|
||||
If you `cargo run` again and refresh the page, you should see "hello, world!"
|
||||
appear in your browser. Congratulations! You just wrote your first asynchronous
|
||||
webserver in Rust.
|
||||
|
||||
You can also inspect the request itself, which contains information such as
|
||||
the request URI, HTTP version, headers, and other metadata. For example, we
|
||||
can print out the URI of the request like this:
|
||||
|
||||
```rust
|
||||
println!("Got request at {:?}", req.uri());
|
||||
```
|
||||
|
||||
You may have noticed that we're not yet doing
|
||||
anything asynchronous when handling the request-- we just respond immediately,
|
||||
so we're not taking advantage of the flexibility that `async fn` gives us.
|
||||
Rather than just returning a static message, let's try proxying the user's
|
||||
request to another website using Hyper's HTTP client.
|
||||
|
||||
We start by parsing out the URL we want to request:
|
||||
|
||||
```rust
|
||||
let url_str = "http://www.rust-lang.org/en-US/";
|
||||
let url = url_str.parse::<Uri>().expect("failed to parse URL");
|
||||
```
|
||||
|
||||
Then we can create a new `hyper::Client` and use it to make a `GET` request,
|
||||
returning the response to the user:
|
||||
|
||||
```rust
|
||||
let res = await!(Client::new().get(url));
|
||||
// Return the result of the request directly to the user
|
||||
println!("request finished --returning response");
|
||||
res
|
||||
```
|
||||
|
||||
`Client::get` returns a `hyper::client::FutureResponse`, which implements
|
||||
`Future<Output = Result<Response, Error>>`
|
||||
(or `Future<Item = Response, Error = Error>` in futures 0.1 terms).
|
||||
When we `await!` that future, an HTTP request is sent out, the current task
|
||||
is suspended, and the task is queued to be continued once a response has
|
||||
become available.
|
||||
|
||||
Now, if you `cargo run` and open `http://127.0.0.1:3000/foo` in your browser,
|
||||
you'll see the Rust homepage, and the following terminal output:
|
||||
|
||||
```
|
||||
Listening on http://127.0.0.1:3000
|
||||
Got request at /foo
|
||||
making request to http://www.rust-lang.org/en-US/
|
||||
request finished-- returning response
|
||||
```
|
||||
|
||||
Congratulations! You just proxied an HTTP request.
|
|
@ -0,0 +1,61 @@
|
|||
## Why Async?
|
||||
|
||||
We all love how Rust allows us to write fast, safe software. But why write
|
||||
asynchronous code?
|
||||
|
||||
Asynchonous code allows us to run multiple tasks concurrently on the same OS
|
||||
thread. In a typical threaded application, if you wanted to download two
|
||||
different webpages at the same time, you would spread the work across two
|
||||
different threads, like this:
|
||||
|
||||
```rust
|
||||
fn get_two_sites() {
|
||||
// Spawn two threads to do work.
|
||||
let thread_one = thread::spawn(|| download("https:://www.foo.com"));
|
||||
let thread_two = thread::spawn(|| download("https:://www.bar.com"));
|
||||
|
||||
// Wait for both threads to complete.
|
||||
thread_one.join();
|
||||
thread_two.join();
|
||||
}
|
||||
```
|
||||
|
||||
This works fine for many applications-- after, all threads were designed
|
||||
to do just this: run multiple different tasks at once. However, they also
|
||||
come with some limitations. There's a lot of overhead involved in the
|
||||
process of switching between different threads and sharing data between
|
||||
threads. Even a thread which just sits and does nothing uses up valuable
|
||||
system resources. These are the costs that asynchronous code is designed
|
||||
to eliminate. We can rewrite the function above using Rust's
|
||||
`async`/`await!` notation, which will allow us to run multiple tasks at
|
||||
once without creating multiple threads:
|
||||
|
||||
```rust
|
||||
async fn get_two_sites() {
|
||||
// Create a two different "futures" which, when run to completion,
|
||||
// will asynchronously download the webpages.
|
||||
let future_one = download_async("https:://www.foo.com");
|
||||
let future_two = download_async("https:://www.bar.com");
|
||||
|
||||
// Run both futures to completion at the same time.
|
||||
join!(future_one, future_two);
|
||||
}
|
||||
```
|
||||
|
||||
Overall, asynchronous applications have the potential to be much faster and
|
||||
use fewer resources than a corresponding threaded implementation. However,
|
||||
there is a cost. Threads are natively supported by the operating system,
|
||||
and using them doesn't require any special programming model-- any function
|
||||
can create a thread, and calling a function that uses threads is usually
|
||||
just as easy as calling any normal function. However, asynchronous functions
|
||||
require special support from the language or libraries in order to work.
|
||||
In Rust, `async fn` creates an asynchronous function which, when called,
|
||||
will return a future which needs to be run to completion in order for the
|
||||
body of the function to execute.
|
||||
|
||||
It's important to remember that traditional threaded applications can be quite
|
||||
effective, and that Rust's small memory footprint and predictability mean that
|
||||
you can get far without ever using `async`. The increased complexity of the
|
||||
asynchronous programming model isn't always worth it, and it's important to
|
||||
consider whether your application would be better served by using a simpler
|
||||
threaded model.
|
|
@ -0,0 +1,138 @@
|
|||
# Pinning
|
||||
|
||||
In order to poll futures, they must be pinned using a special type called
|
||||
`Pin<T>`. If you read the explanation of [the `Future` trait] in the
|
||||
previous section ["Executing `Future`s and Tasks"], you'll recognise
|
||||
`Pin` from the `self: Pin<&mut Self>` in the `Future:poll` method's definition.
|
||||
But what does it mean, and why do we need it?
|
||||
|
||||
## Why Pinning
|
||||
|
||||
Pinning makes it possible to guarantee that an object won't ever be moved.
|
||||
To understand why this is necessary, we need remember how `async`/`await!`
|
||||
works. Consider the following code:
|
||||
|
||||
```rust
|
||||
async {
|
||||
await!(fut_one);
|
||||
await!(fut_two);
|
||||
}
|
||||
```
|
||||
|
||||
Under the hood, this creates an anonymous type that implements `Future`,
|
||||
providing a `poll` method that looks something like this:
|
||||
|
||||
```rust
|
||||
// The `Future` type generated by our `async { ... }` block
|
||||
struct AsyncFuture {
|
||||
fut_one: FutOne,
|
||||
fut_two: FutTwo,
|
||||
state: State,
|
||||
}
|
||||
|
||||
// List of states our `async` block can be in
|
||||
enum State {
|
||||
AwaitingFutOne,
|
||||
AwaitingFutTwo,
|
||||
Done,
|
||||
}
|
||||
|
||||
impl AsyncFuture {
|
||||
fn poll(...) -> Poll<()> {
|
||||
loop {
|
||||
match self.state {
|
||||
State::AwaitingFutOne => match self.fut_one.poll(..) {
|
||||
Poll::Ready(()) => self.state = State::AwaitingFutTwo,
|
||||
Poll::Pending => return Poll::Pending,
|
||||
}
|
||||
State::AwaitingFutTwo => match self.fut_two.poll(..) {
|
||||
Poll::Ready(()) => self.state = State::Done,
|
||||
Poll::Pending => return Poll::Pending,
|
||||
}
|
||||
State::Done => return Poll::Ready(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
When `poll` is first called, it will poll `fut_one`. If `fut_one` can't
|
||||
complete, `AsyncFuture::poll` will return. Future calls to `poll` will pick
|
||||
up where the previous one left off. This process continues until the future
|
||||
is able to successfully complete.
|
||||
|
||||
However, what happens if we have an `async` block that uses references?
|
||||
For example:
|
||||
|
||||
```rust
|
||||
async {
|
||||
let mut x = [0; 128];
|
||||
let read_into_buf_fut = read_into_buf(&mut x);
|
||||
await!(read_into_buf_fut);
|
||||
println!("{:?}", x);
|
||||
}
|
||||
```
|
||||
|
||||
What struct does this compile down to?
|
||||
|
||||
```rust
|
||||
struct ReadIntoBuf<'a> {
|
||||
buf: &'a mut [u8], // points to `x` below
|
||||
}
|
||||
|
||||
struct AsyncFuture {
|
||||
x: [u8; 128],
|
||||
read_into_buf_fut: ReadIntoBuf<'what_lifetime?>,
|
||||
}
|
||||
```
|
||||
|
||||
Here, the `ReadIntoBuf` future holds a reference into the other field of our
|
||||
structure, `x`. However, if `AsyncFuture` is moved, the location of `x` will
|
||||
move as well, invalidating the pointer stored in `read_into_buf_fut.buf`.
|
||||
|
||||
Pinning futures to a particular spot in memory prevents this problem, making
|
||||
it safe to create references to values inside an `async` block.
|
||||
|
||||
## How to Use Pinning
|
||||
|
||||
The `Pin` type wraps pointer types, guaranteeing that the values behind the
|
||||
pointer won't be moved. For example, `Pin<&mut T>`, `Pin<&T>`,
|
||||
`Pin<Box<T>>` all guarantee that `T` won't be moved.
|
||||
|
||||
Most types don't have a problem being moved. These types implement a trait
|
||||
called `Unpin`. Pointers to `Unpin` types can be freely placed into or taken
|
||||
out of `Pin`. For example, `u8` is `Unpin`, so `Pin<&mut T>` behaves just like
|
||||
a normal `&mut T`.
|
||||
|
||||
Some functions require the futures they work with to be `Unpin`. To use a
|
||||
`Future` or `Stream` that isn't `Unpin` with a function that requires
|
||||
`Unpin` types, you'll first have to pin the value using either
|
||||
`Box::pinned` (to create a `Pin<Box<T>>`) or the `pin_utils::pin_mut!` macro
|
||||
(to create a `Pin<&mut T>`). `Pin<Box<Fut>>` and `Pin<&mut Fut>` can both be
|
||||
used as futures, and both implement `Unpin`.
|
||||
|
||||
For example:
|
||||
|
||||
```rust
|
||||
use pin_utils::pin_mut; // `pin_utils` is a handy crate available on crates.io
|
||||
|
||||
// A function which takes a `Future` that implements `Unpin`.
|
||||
fn execute_unpin_future(x: impl Future<Output = ()> + Unpin) { ... }
|
||||
|
||||
let fut = async { ... };
|
||||
execute_unpin_future(fut); // Error: `fut` does not implement `Unpin` trait
|
||||
|
||||
// Pinning with `Box`:
|
||||
let fut = async { ... };
|
||||
let fut = Box::pinned(fut);
|
||||
execute_unpin_future(fut); // OK
|
||||
|
||||
// Pinning with `pin_mut!`:
|
||||
let fut = async { ... };
|
||||
pin_mut!(fut);
|
||||
execute_unpin_future(fut); // OK
|
||||
```
|
||||
|
||||
["Executing `Future`s and Tasks"]: TODO
|
||||
[the `Future` trait]: TODO
|
|
@ -0,0 +1,104 @@
|
|||
# The `Stream` Trait
|
||||
|
||||
The `Stream` trait is similar to `Future` but can yield multiple values before
|
||||
completing, similar to the `Iterator` trait from the standard library:
|
||||
|
||||
```rust
|
||||
trait Stream {
|
||||
/// The type of value yielded by the stream.
|
||||
type Item;
|
||||
|
||||
/// Attempt to resolve the next item in the stream.
|
||||
/// Returns `Poll::Pending` if not ready, `Poll::Ready(Some(x))` if a value
|
||||
/// is ready, and `Poll::Ready(None)` if the stream has completed.
|
||||
fn poll_next(self: Pin<&mut Self>, lw: &LocalWaker)
|
||||
-> Poll<Option<Self::Item>>;
|
||||
}
|
||||
```
|
||||
|
||||
One common example of a `Stream` is the `Receiver` for the channel type from
|
||||
the `futures` crate. It will yield `Some(val)` every time a value is sent
|
||||
from the `Sender` end, and will yield `None` once the `Sender` has been
|
||||
dropped and all pending messages have been received:
|
||||
|
||||
```rust
|
||||
use futures::channel::mpsc;
|
||||
use futures::prelude::*;
|
||||
|
||||
let fut = async {
|
||||
let (tx, rx) = mpsc::channel(BUFFER_SIZE);
|
||||
await!(tx.send(1)).unwrap();
|
||||
await!(tx.send(2)).unwrap();
|
||||
drop(tx);
|
||||
|
||||
// `StreamExt::next` is similar to `Iterator::next`, but returns a
|
||||
// type that implements `Future<Output = Option<T>>`.
|
||||
assert_eq!(Some(1), await!(rx.next()));
|
||||
assert_eq!(Some(2), await!(rx.next()));
|
||||
assert_eq!(None, await!(rx.next()));
|
||||
};
|
||||
```
|
||||
|
||||
## Patterns: Iteration and Concurrency
|
||||
|
||||
Similar to synchronous `Iterator`s, there are many different ways to iterate
|
||||
over and process the values in a `Stream`. There are combinator-style methods
|
||||
such as `map`, `filter`, and `fold`, and their early-exit-on-error cousins
|
||||
`try_map`, `try_filter`, and `try_fold`.
|
||||
|
||||
Unfortunately, `for` loops are not yet usable with `Stream`s, but for
|
||||
imperative-style code, `while let` and `.for_each` are available:
|
||||
|
||||
```rust
|
||||
use futures::prelude::*;
|
||||
|
||||
let fut = async {
|
||||
let mut stream: impl Stream<Item = Result<i32, io::Error>> = ...;
|
||||
|
||||
// processing with `try_for_each`:
|
||||
await!(stream.try_for_each(async |item| {
|
||||
// handle `item`
|
||||
Ok(())
|
||||
}))?;
|
||||
|
||||
// processing with `while let`:
|
||||
while let Some(item) = await!(stream.try_next())? {
|
||||
// handle `item`
|
||||
}
|
||||
|
||||
...
|
||||
|
||||
Ok(())
|
||||
};
|
||||
```
|
||||
|
||||
However, if we're just processing one element at a time, we're potentially
|
||||
leaving behind opportunity for concurrency, which is, after all, why we're
|
||||
writing async code in the first place. To process multiple items from a stream
|
||||
concurrently, use the `for_each_concurrent` and `try_for_each_concurrent`
|
||||
methods:
|
||||
|
||||
```rust
|
||||
use futures::prelude::*;
|
||||
|
||||
let fut = async {
|
||||
let mut stream: impl Stream<Item = Result<i32, io::Error>> = ...;
|
||||
|
||||
await!(stream.try_for_each_concurrent(MAX_CONCURRENT_JUMPERS, async |num| {
|
||||
await!(jump_n_times(num))?;
|
||||
await!(report_jumps(num))?;
|
||||
Ok(())
|
||||
})?;
|
||||
|
||||
...
|
||||
Ok(())
|
||||
};
|
||||
```
|
||||
|
||||
This approach allows up to `MAX_CONCURRENT_JUMPERS` to all be jumping at once
|
||||
(or performing any operation on the items, for that matter-- the API isn't
|
||||
strictly tied to jumping). If you want to allow an unlimited number of
|
||||
operations at once, you can use `None` rather than `MAX_CONCURRENT_...`, but
|
||||
beware that if `stream` comes from untrusted user input, this can allow
|
||||
badly behaved clients to overload the system with too many simultaneous
|
||||
requests.
|
Loading…
Reference in New Issue