diff --git a/src/1_futures_in_rust.md b/src/1_futures_in_rust.md index 0167de4..6e056b0 100644 --- a/src/1_futures_in_rust.md +++ b/src/1_futures_in_rust.md @@ -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, it’s given a clone of the Waker object created by -the executor. Since this is a shared object (e.g. an `Arc`), 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, it’s given a clone of the Waker + object created by the executor. Since this is a shared object (e.g. an + `Arc`), 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 @@ -140,7 +144,7 @@ object). 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 -communicate with one another directly.**. +communicate with one another directly.** This design is what gives the futures framework it's power and flexibility and allows the Rust standard library to provide an ergonomic, zero-cost abstraction for us to use. diff --git a/src/conclusion.md b/src/conclusion.md index 17d6dec..e8eb289 100644 --- a/src/conclusion.md +++ b/src/conclusion.md @@ -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`. 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.