clearified and added more info in response to #50
This commit is contained in:
7
.vscode/settings.json
vendored
7
.vscode/settings.json
vendored
@@ -1,8 +1,11 @@
|
||||
{
|
||||
"spellright.language": [],
|
||||
"spellright.language": [
|
||||
"en"
|
||||
],
|
||||
"spellright.documentTypes": [
|
||||
"latex",
|
||||
"plaintext"
|
||||
"plaintext",
|
||||
"markdown"
|
||||
],
|
||||
"cSpell.enabled": true
|
||||
}
|
||||
@@ -122,9 +122,8 @@ 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
|
||||
perspective. Here is the life cycle:
|
||||
|
||||
- A Waker is created by the **executor.** A common, but not required, method is
|
||||
to create a new Waker for each Future that is registered with the executor.
|
||||
- When a future is registered with an executor, it’s given a clone of the Waker
|
||||
- A Waker is created by the **executor.**
|
||||
- When a future is polled the first time by the executor, it’s given a clone of the Waker
|
||||
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,
|
||||
anything that calls _any_ clone of the original Waker will wake the particular
|
||||
@@ -132,15 +131,11 @@ perspective. Here is the life cycle:
|
||||
- The future clones the Waker and passes it to the reactor, which stores it to
|
||||
use later.
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
The Waker object implements everything that we associate with
|
||||
[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).
|
||||
>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.
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
@@ -27,8 +27,7 @@ extend the ecosystem with new leaf-level tasks.
|
||||
## The Context type
|
||||
|
||||
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
|
||||
task-local storage and provide space for debugging hooks in later iterations.
|
||||
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.
|
||||
|
||||
## 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
|
||||
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
|
||||
article written by Adam Schwalm called [Exploring Dynamic Dispatch in Rust](https://alschwalm.com/blog/static/2017/03/07/exploring-dynamic-dispatch-in-rust/).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user