The async book has an example of a Join future that joins 2 subtasks. It uses the simplified Future
trait that just takes a wake: fn()
. When the real Future
definition is introduced, they explain why this becomes &mut Context<'_>
:
Secondly,
wake: fn()
has changed to&mut Context<'_>
. InSimpleFuture
, we used a call to a function pointer (fn()
) to tell the future executor that the future in question should be polled. However, sincefn()
is just a function pointer, it can't store any data about whichFuture
calledwake
.
In a real-world scenario, a complex application like a web server may have thousands of different connections whose wakeups should all be managed separately. The
Context
type solves this by providing access to a value of typeWaker
, which can be used to wake up a specific task.
This made sense to me and made me expect something: that Context
or Waker
would require you to pass the Future
's self
into some method in order to link the two together. This way if you implement Join
, you give the first subtask a context/waker that knows to wake up the first subtask and you give the second subtask a context/waker that knows to wake up the second subtask. But this never happens.
In the next section of the book they show how you use waker
:
shared_state.waker = Some(cx.waker().clone());
Nowhere do we associate the current future with the waker. My first thought was maybe this somehow happens earlier, e.g. when the Context
is created maybe its already tied to the particular Future
. But the only way to create a Context
is from a Waker
. Waker
can only be created from RawWaker
... which holds executor specific raw data. Does an efficient Join
require executor specific code? Not hugely important if Join is only on two things, but if it were a vector of 1000 things it would obviously matter a lot.