contd
This commit is contained in:
@@ -13,6 +13,78 @@ information that will help demystify some of the concepts we encounter.
|
|||||||
Actually, after going through these concepts, implementing futures will seem
|
Actually, after going through these concepts, implementing futures will seem
|
||||||
pretty simple. I promise.
|
pretty simple. I promise.
|
||||||
|
|
||||||
|
## Futures
|
||||||
|
|
||||||
|
So what is a future?
|
||||||
|
|
||||||
|
A future is a representation of some operation which will complete in the
|
||||||
|
future.
|
||||||
|
|
||||||
|
Now, when we talk about futures I find it useful to make a distinction between
|
||||||
|
**non-leaf** futures and **leaf** futures early on because in practice they're
|
||||||
|
pretty different from one another.
|
||||||
|
|
||||||
|
### Leaf futures
|
||||||
|
|
||||||
|
Runtimes create _leaf futures_ which represents a resource like a socket.
|
||||||
|
|
||||||
|
```rust, ignore, noplaypen
|
||||||
|
// stream is a **leaf-future**
|
||||||
|
let mut stream = tokio::net::TcpStream::connect("127.0.0.1:3000");
|
||||||
|
```
|
||||||
|
|
||||||
|
Operations on these resources, like a `Read` on a socket, will be non-blocking
|
||||||
|
and return a future which we call a leaf future since it's the future which
|
||||||
|
we're actually waiting on.
|
||||||
|
|
||||||
|
It's unlikely that you'll implement a leaf future yourself unless you're writing
|
||||||
|
a runtime, but we'll go through how they're constructed in this book as well.
|
||||||
|
|
||||||
|
It's also unlikely that you'll pass a leaf-future to a runtime and run it to
|
||||||
|
completion alone as you'll understand by reading the next paragraph.
|
||||||
|
|
||||||
|
### Non-leaf-futures
|
||||||
|
|
||||||
|
Non-leaf-futures is the kind of futures we as _users_ of a runtime writes
|
||||||
|
ourselves using the `async` keyword to create a **task** which can be run on the
|
||||||
|
executor.
|
||||||
|
|
||||||
|
This is an important distinction since these futures represents a
|
||||||
|
_set of operations_. Often, such a task will `await` a leaf future as one of
|
||||||
|
many operations to complete the task.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
// Non-leaf-future
|
||||||
|
let non_leaf = async {
|
||||||
|
let mut stream = TcpStream::connect("127.0.0.1:3000").await.unwrap();// <- yield
|
||||||
|
println!("connected!");
|
||||||
|
let result = stream.write(b"hello world\n").await; // <- yield
|
||||||
|
println!("message sent!");
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
The key to these tasks is that they're able to yield control to the runtime's
|
||||||
|
scheduler and then resume execution again where it left off at a later point.
|
||||||
|
|
||||||
|
In contrast to leaf futures, these kind of futures does not themselves represent
|
||||||
|
an I/O resource. When we poll these futures we either run some code or we yield
|
||||||
|
to the scheduler while waiting for some resource to signal us that it's ready so
|
||||||
|
we can resume where we left off.
|
||||||
|
|
||||||
|
### Runtimes
|
||||||
|
|
||||||
|
Quite a bit of complexity attributed to `Futures` are actually complexity rooted
|
||||||
|
in runtimes. Creating an efficient runtime is hard.
|
||||||
|
|
||||||
|
Learning how to use one correctly can require quite a bit of effort as well, but
|
||||||
|
you'll see that there are several similarities between these kind of runtimes so
|
||||||
|
learning one makes learning the next much easier.
|
||||||
|
|
||||||
|
The difference between Rust and other languages is that you have to make an
|
||||||
|
active choice when it comes to picking a runtime. Most often you'll just use
|
||||||
|
the one provided for you.
|
||||||
|
|
||||||
## Async in Rust
|
## Async in Rust
|
||||||
|
|
||||||
Let's get some of the common roadblocks out of the way first.
|
Let's get some of the common roadblocks out of the way first.
|
||||||
@@ -57,41 +129,8 @@ concepts you need to know about. This is not true. In practice today you'll only
|
|||||||
interface with the runtime, which provides leaf futures which actually wait for
|
interface with the runtime, which provides leaf futures which actually wait for
|
||||||
some I/O operation, and the executor where
|
some I/O operation, and the executor where
|
||||||
|
|
||||||
## Futures
|
|
||||||
|
|
||||||
Now, when we talk about futures I find it useful to make a distinction between
|
## Bonus section
|
||||||
non-leaf futures and **leaf** futures.
|
|
||||||
|
|
||||||
Runtimes create leaf futures which represents a resource like a socket.
|
|
||||||
Operations on these resources, like a `Read` on a socket, will be non-blocking
|
|
||||||
and instead return a future which we call a leaf future since it's the future
|
|
||||||
which we're actually waiting on.
|
|
||||||
|
|
||||||
It's unlikely that you'll implement a leaf future yourself unless you're writing
|
|
||||||
a runtime, but we'll go through how they're constructed in this book as well.
|
|
||||||
|
|
||||||
Non-leaf futures is the kind of futures we as users of a runtime writes
|
|
||||||
ourselves using the `async` keyword to create a task which can be run on the
|
|
||||||
executor. Often, such a task will `await` a leaf future as one of many
|
|
||||||
operations to complete the task.
|
|
||||||
|
|
||||||
The key to these tasks is that they're able to yield control to the runtime's
|
|
||||||
scheduler and then resume execution again where it left off at a later point.
|
|
||||||
|
|
||||||
In contrast to leaf futures, these kind of futures is not themselves
|
|
||||||
waiting on some I/O resource.
|
|
||||||
|
|
||||||
Quite a bit of complexity attributed to `Futures` are actually complexity rooted
|
|
||||||
in runtimes. Creating an efficient runtime is hard.
|
|
||||||
|
|
||||||
Learning how to use one correctly can require quite a bit of effort as well, but you'll see that there are several similarities between these kind of runtimes so
|
|
||||||
learning one makes learning the next much easier.
|
|
||||||
|
|
||||||
The difference between Rust and other languages is that you have to make an
|
|
||||||
active choice when it comes to picking a runtime. Most often you'll just use
|
|
||||||
the one provided for you.
|
|
||||||
|
|
||||||
## Before we move on
|
|
||||||
|
|
||||||
If you find the concepts of concurrency and async programming confusing in
|
If you find the concepts of concurrency and async programming confusing in
|
||||||
general, I know where you're coming from and I have written some resources to
|
general, I know where you're coming from and I have written some resources to
|
||||||
|
|||||||
@@ -1,12 +1,14 @@
|
|||||||
# Trait objects and fat pointers
|
# Waker and Context
|
||||||
|
|
||||||
> **Relevant for:**
|
> **Relevant for:**
|
||||||
>
|
>
|
||||||
> - Understanding how the Waker object is constructed
|
> - Understanding how the Waker object is constructed
|
||||||
> - Getting a basic feel for "type erased" objects and what they are
|
> - Learning how the runtime know when a leaf-future can resume
|
||||||
> - Learning the basics of dynamic dispatch
|
> - Learning the basics of dynamic dispatch and trait objects
|
||||||
|
|
||||||
## Trait objects and dynamic dispatch
|
## The Waker
|
||||||
|
|
||||||
|
The `Waker` trait is an interface where a
|
||||||
|
|
||||||
One of the most confusing things we encounter when implementing our own `Futures`
|
One of the most confusing things we encounter when implementing our own `Futures`
|
||||||
is how we implement a `Waker` . Creating a `Waker` involves creating a `vtable`
|
is how we implement a `Waker` . Creating a `Waker` involves creating a `vtable`
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
[Introduction](./introduction.md)
|
[Introduction](./introduction.md)
|
||||||
|
|
||||||
- [Some background information](./1_background_information.md)
|
- [Some background information](./1_background_information.md)
|
||||||
- [Trait objects and fat pointers](./2_trait_objects.md)
|
- [Waker and Context](./2_waker_context.md)
|
||||||
- [Generators](./3_generators_pin.md)
|
- [Generators](./3_generators_pin.md)
|
||||||
- [Pin](./4_pin.md)
|
- [Pin](./4_pin.md)
|
||||||
- [Futures - our main example](./6_future_example.md)
|
- [Futures - our main example](./6_future_example.md)
|
||||||
|
|||||||
@@ -2,15 +2,21 @@
|
|||||||
|
|
||||||
This book aims to explain `Futures` in Rust using an example driven approach.
|
This book aims to explain `Futures` in Rust using an example driven approach.
|
||||||
|
|
||||||
The goal is to get a better understanding of `Futures` by implementing a toy
|
The goal is to get a better understanding of "async" in Rust by creating a toy
|
||||||
`Reactor`, a very simple `Executor` and our own `Futures`.
|
runtime consisting of a `Reactor` and an `Executor`, and our own futures which
|
||||||
|
we can run concurrently.
|
||||||
|
|
||||||
We'll start off a bit differently than most other explanations. Instead of
|
We'll start off a bit differently than most other explanations. Instead of
|
||||||
deferring some of the details about what's special about futures in Rust we
|
deferring some of the details about what `Futures` are and how they're
|
||||||
try to tackle that head on first. We'll be as brief as possible, but as thorough
|
implemented, we tackle that head on first.
|
||||||
as needed. This way, most questions will be answered and explored up front.
|
|
||||||
|
|
||||||
We'll end up with futures that can run an any executor like `tokio` and `async_str`.
|
I learn best when I can take basic understandable concepts and build piece by
|
||||||
|
piece of these basic building blocks until everything is understood. This way,
|
||||||
|
most questions will be answered and explored up front and the conclusions later
|
||||||
|
on seems natural.
|
||||||
|
|
||||||
|
I've limited myself to a 200 line main example so that we need keep
|
||||||
|
this fairly brief.
|
||||||
|
|
||||||
In the end I've made some reader exercises you can do if you want to fix some
|
In the end I've made some reader exercises you can do if you want to fix some
|
||||||
of the most glaring omissions and shortcuts we took and create a slightly better
|
of the most glaring omissions and shortcuts we took and create a slightly better
|
||||||
|
|||||||
Reference in New Issue
Block a user