Is a waker unique per future, or per thread?

In a single-threaded application, does calling the waker poll all futures, only futures which last returned Poll::NotReady, or does it only poll the future which is associated with the context.waker() call?

Also, if I needed to make an async callback, would it be "best" to:

  1. impl Future or Stream for a struct
  2. in the impl, make sure to store the waker inside the struct using the context
  3. call poll on the struct once to load the waker in the struct
  4. from the closure which created the struct, grab the waker and store it in a hashmap to be called once relevant data (or a timeout) occurs

?

1 Like

Your question is rather broad and depends on actual runtime implementation.
But normally waker is associated with runtime context which is likely to be unique to single runtime. (In case of multi-threaded tokio it might be a bit more complicated)

In general runtime should poll task that invoked wake only.
But it is really up to particular runtime implementation.

async callback

You really need to elaborate what you want to achieve.
This is rather unclear term you use here

Part of the purpose of having an "async callback" is to send a packet with a particular ID outbound, and then await a response from the other end. The future will return Poll::NotReady when it is first polled, because no response has yet arrived. Then, it would be the duty of an async socket-listener to awaken the future and poll it to continue the future. I would also like the future to timeout after some time, so I'd have to wrap the send_packet_fn in a Timeout struct as provided by tokio

send_packet_fn(my_bytes).await.and_then(|res| println!("Response packet received!"));

The contract for Future::poll is that it returns “quickly” in all cases and, if it returns Pending, it has made some kind of arrangement for the waker to be triggered when it can make progress. In particular, you need to correctly handle poll being called again before the waker has been activated.

Usually, whatever code is actively watching for incoming events will have a queue of wakers to notify when an event comes in, and the Future struct will have a way to enqueue any waker it’s given via poll.

If you’re just waiting for other Futures to be ready, you can rely on their poll implementations registering the waker wherever it’s needed.

In short, each wakers are associated to some task.

Futures are meant to be combined to form bigger future. When we stop combining futures and pass it to runtime via api like tokio::spawn(), now it's called task. You can think tasks as asynchronous threads. Tasks usually be executed within fixed amount of OS threads, either in parallel or in serial though concurrently, similar to how OS threads are executed within fixed amount of CPU cores.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.