fix: Improved the explanation of Wakers.
The original explanation of Wakers sort-of implied that there was exactly one Waker for all futures (each executor creates one, then passes clones of that to all registered futures). The new phrasing makes it clear that isn't the case. Also added another couple of paragraphs to the conclusion to really expand on that.
This commit is contained in:
@@ -122,11 +122,15 @@ 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 Waker is created by the **executor.** A common, but not required, method is
|
||||||
- When a future is registered with an executor, it’s given a clone of the Waker object created by
|
to create a new Waker for each Future that is registered with the executor.
|
||||||
the executor. Since this is a shared object (e.g. an `Arc<T>`), all clones actually point to the
|
- When a future is registered with an executor, it’s given a clone of the Waker
|
||||||
same underlying object
|
object created by the executor. Since this is a shared object (e.g. an
|
||||||
- The future clones the Waker and passes it to the reactor, which stores it to use later.
|
`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
|
||||||
|
Future that was registered to it.
|
||||||
|
- 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
|
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
|
the future via the Waker that it stored. This action will do what is necessary to get the executor
|
||||||
|
|||||||
@@ -11,6 +11,45 @@ I'll leave you with some suggestions for exercises if you want to explore a litt
|
|||||||
|
|
||||||
Until next time!
|
Until next time!
|
||||||
|
|
||||||
|
## Further notes and comments
|
||||||
|
|
||||||
|
In [Chapter 2](1_futures_in_rust.md), I mentioned that it is common for the
|
||||||
|
executor to create a new Waker for each Future that is registered with the
|
||||||
|
executor, but that the Waker is a shared object similar to a `Arc<T>`. One of
|
||||||
|
the reasons for this design is that it allows different Reactors the ability to
|
||||||
|
Wake a Future. As an example of how this can be used, consider how you could
|
||||||
|
create a new type of Future that has the ability to be canceled. A method of
|
||||||
|
doing this would be to add an
|
||||||
|
[`AtomicBool`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html)
|
||||||
|
to the instance of the future, and an extra method called `cancel()`. The
|
||||||
|
`cancel()` method will first set the
|
||||||
|
[`AtomicBool`](https://doc.rust-lang.org/std/sync/atomic/struct.AtomicBool.html)
|
||||||
|
to signal that the future is now canceled, and then immediately call instance's
|
||||||
|
own copy of the Waker. Once the executor starts executing the Future, the
|
||||||
|
_Future_ will know that it was canceled, and will do the appropriate cleanup
|
||||||
|
actions to terminate itself.
|
||||||
|
|
||||||
|
The main reason for designing the Future in this manner is because we don't have
|
||||||
|
to modify either the Executor or the other Reactors; they are all oblivious to
|
||||||
|
the change. The only possible issue is with the design of the Future itself; a
|
||||||
|
Future that is canceled still needs to terminate correctly according to the
|
||||||
|
rules outlined in the docs for
|
||||||
|
[`Future`](https://doc.rust-lang.org/std/future/trait.Future.html). That means
|
||||||
|
that it can't just delete it's resources and then sit there; it needs to return
|
||||||
|
a value. It is up to you to decide if a canceled future will return
|
||||||
|
[`Pending`](https://doc.rust-lang.org/std/task/enum.Poll.html#variant.Pending)
|
||||||
|
forever, or if it will return a value in
|
||||||
|
[`Ready`](https://doc.rust-lang.org/std/task/enum.Poll.html#variant.Ready). Just
|
||||||
|
be aware that if other Futures are `await`ing it, they won't be able to start
|
||||||
|
until [`Ready`](https://doc.rust-lang.org/std/task/enum.Poll.html#variant.Ready)
|
||||||
|
is returned. A common technique for cancelable Futures is to have them return a
|
||||||
|
Result with an error that signals the Future was canceled; that will permit any
|
||||||
|
Futures that are awaiting the canceled Future a chance to progress, with the
|
||||||
|
knowledge that the Future they depended on was canceled. There are additional
|
||||||
|
concerns as well, but beyond the scope of this book. Read the documentation and
|
||||||
|
code for the [`futures`](https://crates.io/crates/futures) crate for a better
|
||||||
|
understanding of what the concerns are.
|
||||||
|
|
||||||
## Reader exercises
|
## Reader exercises
|
||||||
|
|
||||||
So our implementation has taken some obvious shortcuts and could use some improvement.
|
So our implementation has taken some obvious shortcuts and could use some improvement.
|
||||||
|
|||||||
Reference in New Issue
Block a user