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:
Cem Karan
2020-12-27 12:47:51 -05:00
parent e1d7fa8d67
commit 064a9b8077
2 changed files with 48 additions and 5 deletions

View File

@@ -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
perspective. Here is the life cycle:
- A Waker is created by the **executor**
- 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 `Arc<T>`), all clones actually point to the
same underlying object
- The future clones the Waker and passes it to the reactor, which stores it to use later.
- 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, its 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
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
the future via the Waker that it stored. This action will do what is necessary to get the executor

View File

@@ -11,6 +11,45 @@ I'll leave you with some suggestions for exercises if you want to explore a litt
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
So our implementation has taken some obvious shortcuts and could use some improvement.