So, if we visualize the degree of dependency between the different parts of our runtime, our current design could be described this way:

Figure 8.5 – Tight coupling between reactor and executor

If we want a loose coupling between the reactor and executor, we need an interface provided to signal the executor that it should wake up when an event that allows a future to progress has occurred. It’s no coincidence that this type is called Waker (https://doc.rust-lang.org/stable/std/task/struct.Waker.html) in Rust’s standard library. If we change our visualization to reflect this, it will look something like this:

Figure 8.6 – A loosely coupled reactor and executor

It’s no coincidence that we land on the same design as what we have in Rust today. It’s a minimal design from Rust’s point of view, but it allows for a wide variety of runtime designs without laying too many restrictions for the future.

Note

Even though the design is pretty minimal today from a language perspective, there are plans to stabilize more async-related traits and interfaces in the future.

Rust has a working group tasked with including widely used traits and interfaces in the standard library, which you can find more information about here: https://rust-lang.github.io/wg-async/welcome.html. You can also get an overview of items they work on and track their progress here: https://github.com/orgs/rust-lang/projects/28/views/1.

Maybe you even want to get involved (https://rust-lang.github.io/wg-async/welcome.html#-getting-involved) in making async Rust better for everyone after reading this book?

If we change our system diagram to reflect the changes we need to make to our runtime going forward, it will look like this:

Figure 8.7 – Executor and reactor: final design

We have two parts that have no direct dependency on each other. We have an Executor that schedules tasks and passes on a Waker when polling a future that eventually will be caught and stored by the Reactor. When the Reactor receives a notification that an event is ready, it locates the Waker associated with that task and calls Wake::wake on it.

This enables us to:

  • Run several OS threads that each have their own executor, but share the same reactor
  • Have multiple reactors that handle different kinds of leaf futures and make sure to wake up the correct executor when it can progress

So, now that we have an idea of what to do, it’s time to start writing it in code.

Leave a Reply

Your email address will not be published. Required fields are marked *