Files
books-futures-explained/src/conclusion.md
2020-02-04 01:31:02 +01:00

5.5 KiB

Conclusion and exercises

Congratulations. Good job! If you got this far you must have stayed with me all the way. I hope you enjoyed the ride!

I'll leave you with some predictions and a set of exercises I'm suggesting for those interested.

Futures will be more ergonomic to use with time. For example, instead of having to create a RawWaker and so on, the Waker will also be possible to implement as a normal Trait. It's probably going to be pretty similar to ArcWake.

There will probably be several more improvements like this, but since relatively few people will actually need implement leaf Futures compared to those that use them, focus will first and foremost be on how ergonomic it's to work with futures inside async/await functions and blocks.

It will still take some time for the ecosystem to migrate over to Futures 3.0 but since the advantages are so huge, it will not be a split between libraries using Futures 1.0 and libraries using Futures 3.0 for long.

Reader exercises

So our implementation has taken some obvious shortcuts and could use some improvement. Actually digging into the code and try things yourself is a good way to learn. Here are some good exercises if you want to explore more:

Avoid thread::park

The big problem using Thread::park and Thread::unpark is that the user can access these same methods from their own code. Try to use another method to suspend our thread and wake it up again on our command. Some hints:

  • Check out CondVars, here are two sources Wikipedia and the docs for CondVar
  • Take a look at crates that help you with this exact problem like Crossbeam (specifically the Parker)

Avoid wrapping the whole Reactor in a mutex and pass it around

First of all, protecting the whole Reactor and passing it around is overkill. We're only interested in synchronizing some parts of the information it contains. Try to refactor that out and only synchronize access to what's really needed.

  • Do you want to pass around a reference to this information using an Arc?
  • Do you want to make a global Reactor so it can be accessed from anywhere?

Next , using a Mutex as a synchronization mechanism might be overkill since many methods only reads data.

Avoid creating a new Waker for every event

Right now we create a new instance of a Waker for every event we create. Is this really needed?

  • Could we create one instance and then cache it (see this article from u/sjepang)?
    • Should we cache it in thread_local! storage?
    • Or should be cache it using a global constant?

Could we implement more methods on our executor?

What about CPU intensive tasks? Right now they'll prevent our executor thread from progressing an handling events. Could you create a thread pool and create a method to send such tasks to the thread pool instead together with a Waker which will wake up the executor thread once the CPU intensive task is done?

In both async_std and tokio this method is called spawn_blocking, a good place to start is to read the documentation and the code thy use to implement that.

Building a better exectuor

Right now, we can only run one and one future. Most runtimes has a spawn function which let's you start off a future and await it later so you can run multiple futures concurrently.

As I'm writing this @stjepan is writing a blog series about implementing your own executors, and he just released a post on how to accomplish just this you can visit here. He knows what he's talking about so I recommend following that.

In the bonus_spawn branch of the example repository you can also find an extremely simplified (and worse) way of accomplishing the same in only a few lines of code.

Further reading

There are many great resources for further study. In addition to the RFCs and articles I've already linked to in the book, here are some of my suggestions:

The official Asyc book

The async_std book

Aron Turon: Designing futures for Rust

Steve Klabnik's presentation: Rust's journey to Async/Await

The Tokio Blog

Stjepan's blog with a series where he implements an Executor

Jon Gjengset's video on The Why, What and How of Pinning in Rust