clearified and added more info in response to #50

This commit is contained in:
Carl Fredrik Samson
2022-12-07 16:48:48 +01:00
parent ff8db7c549
commit df5613c87e
3 changed files with 16 additions and 15 deletions

View File

@@ -1,8 +1,11 @@
{ {
"spellright.language": [], "spellright.language": [
"en"
],
"spellright.documentTypes": [ "spellright.documentTypes": [
"latex", "latex",
"plaintext" "plaintext",
"markdown"
], ],
"cSpell.enabled": true "cSpell.enabled": true
} }

View File

@@ -122,25 +122,20 @@ The `Waker` is how the reactor tells the executor that a specific Future is read
understand the life cycle and ownership of a Waker, you'll understand how futures work from a user's understand the life cycle and ownership of a Waker, you'll understand how futures work from a user's
perspective. Here is the life cycle: perspective. Here is the life cycle:
- A Waker is created by the **executor.** A common, but not required, method is - A Waker is created by the **executor.**
to create a new Waker for each Future that is registered with the executor. - When a future is polled the first time by the executor, its given a clone of the Waker
- When a future is registered with an executor, its given a clone of the Waker
object created by the executor. Since this is a shared object (e.g. an object created by the executor. Since this is a shared object (e.g. an
`Arc<T>`), all clones actually point to the same underlying object. Thus, `Arc<T>`), all clones actually point to the same underlying object. Thus,
anything that calls _any_ clone of the original Waker will wake the particular anything that calls _any_ clone of the original Waker will wake the particular
Future that was registered to it. Future that was registered to it.
- The future clones the Waker and passes it to the reactor, which stores it to - The future clones the Waker and passes it to the reactor, which stores it to
use later. use later.
You could think of a "future" like a channel for the `Waker`: The channel starts with the future that's polled the first time by the executor and is passed a handle to a `Waker`. It ends in a leaf-future which passes that handle to the reactor.
At some point in the future, the reactor will decide that the future is ready to run. It will wake >Note that the `Waker` is wrapped in a rather uninteresting `Context` struct which we will learn more about later. The interesting part is the `Waker` that is passed on.
the future via the Waker that it stored. This action will do what is necessary to get the executor
in a position to poll the future.
The Waker object implements everything that we associate with At some point in the future, the reactor will decide that the future is ready to run. It will wake the future via the Waker that it stored. This action will do what is necessary to get the executor in a position to poll the future. We'll go into more detail on Wakers in the [Waker and Context chapter.](3_waker_context.md#understanding-the-waker)
[task](https://doc.rust-lang.org/std/task/index.html). The object is specific to the type of
executor in use, but all Wakers share a similar interface (it's not a trait because
embedded systems can't handle trait objects, but a useful abstraction is to think of it as a trait
object).
Since the interface is the same across all executors, reactors can _in theory_ be completely Since the interface is the same across all executors, reactors can _in theory_ be completely
oblivious to the type of the executor, and vice-versa. **Executors and reactors never need to oblivious to the type of the executor, and vice-versa. **Executors and reactors never need to

View File

@@ -27,8 +27,7 @@ extend the ecosystem with new leaf-level tasks.
## The Context type ## The Context type
As the docs state as of now this type only wraps a `Waker`, but it gives some As the docs state as of now this type only wraps a `Waker`, but it gives some
flexibility for future evolutions of the API in Rust. The context can for example hold flexibility for future evolutions of the API in Rust. The context can for example hold task-local storage and provide space for debugging hooks in later iterations.
task-local storage and provide space for debugging hooks in later iterations.
## Understanding the `Waker` ## Understanding the `Waker`
@@ -37,6 +36,10 @@ is how we implement a `Waker` . Creating a `Waker` involves creating a `vtable`
which allows us to use dynamic dispatch to call methods on a _type erased_ trait which allows us to use dynamic dispatch to call methods on a _type erased_ trait
object we construct ourselves. object we construct ourselves.
The `Waker` implementation is specific to the type of executor in use, but all Wakers share a similar interface. It's useful to think of it as a `Trait`. It's not implemented as such since that would require us to treat it like a trait object which _forces_ dynamic dispatch for all users (which in turn degrades performance when it's not needed). In addition, even if we accepted the performance cost forced on all users, it either restricts the API severely by requiring a `&dyn Waker` like trait object, or would require an `Arc<dyn Waker>` which in turn requires a heap allocation which a lot of embedded-like systems can't do.
Having the Waker implemented the way it is supports users creating a statically-allocated wakers and even more exotic mechanisms to on platforms where that makes sense.
>If you want to know more about dynamic dispatch in Rust I can recommend an >If you want to know more about dynamic dispatch in Rust I can recommend an
article written by Adam Schwalm called [Exploring Dynamic Dispatch in Rust](https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/). article written by Adam Schwalm called [Exploring Dynamic Dispatch in Rust](https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/).