When will the waker be cloned and waked in juliex?

Hello, guys! I’m learning the Rust async/await and right now reading the source code of the juliex.

It’s well-structured but I got confused somewhere:

Shortly before finishing polling the future, somebody, somewhere, somehow, cloned the waker (called the clone_raw function), waked it up after the polling and started polling again.

I have no idea what happened at all, is it something about the compiler internally? or I just missed something in the code?

It is the Futures job to clone the Waker the times poll is called and call wake when ready for the final call to poll.

Maybe you need to look at the source of Waker to see how it links in with the executor you link.

Can you please clear the meaning of the sentence, it’s Future’s job to clone the waker? AFAIK, Future is a trait which doesn’t do anything besides what we definite. And we usually create a new Future using async keyword and will not involve waker at all.

I’m testing with the example provided by juliex and it obviously doesn’t clone and wake anything.

Btw, I just read the Future documents again and it have now much more details than the last time I read. It says that

When a future is not ready yet, poll returns Poll::Pending and stores a clone of the Waker copied from the current Context. This Waker is then woken once the future can make progress.

It seems that the compiler will handle the cloning and waking staffs itself? I’m not sure if I get it right or not.

Take the example;

async fn echo_on(stream: TcpStream) -> io::Result<()> { ... }

The compiler will change this into;

fn echo_on(stream: TcpStream) -> impl Future<Output=io::Result<()>> { ... }

It will make an unnamed structure that implements the Future trait.

Ok, I think I did miss something important. I was testing the juliex with the romio and actually the Future is from romio. So probably romio is the one who cloned a waker and I will check it out.

After all, I still don’t think that Rust compiler will wake the Future itself. Because the Rust team had already announced that they would not include any async runtime in the standard library.

Yes, there will be an unnamed struct or something for each async function we write. But the waker is actually something to wake up the task and repoll the future. It’s not required for constructing a new future.

If you implement a future, you are responsible to wake up the task when you are ready to do some more work. So normally what’s really handy, is when you are waiting on an inner future, you can just poll them and if they return Poll::Pending, they will wake up the task.

So obviously, someone needs to be at the end of the chain. So that’s why for IO there will be a “reactor” which will take care of registering tasks that need to be woken up when more IO is available (and deal with epoll and the like). So when the poll goes down the chain of futures waiting on eachother, the waker is passed down, and when it arrives at say a TcpStream, it will pass the waker to a reactor (Tokio or Romio) and when IO arrives, that reactor calls waker.wake() to wake up the task.

Now the task is polled top down again. Something polls your future, or async block, and you are await!ng the TcpStream, so your call to await! polls it, and since the task got woken by the reactor, it means there will be new data which can now bubble up all the way. Thus there are no unnecessary polls, like polling in a loop.

Thank you for your detailed explanation!

Now anything makes sense to me. When we polling a future, we actually polling a chain of them. The waker will be passed through all the way down to the bottom, registered by a reactor, in my case, romio and handled separately. Everything is done by the executor and reactor, Rust doesn’t manipulate anything.

Exactly, and if ever you make a future for something that you can not defer to someone else, you have to take care of waking.

Say you create something that spawns a separate thread for doing an expensive computation, and other code can await! your future (poll it) and you will return Pending if the computation is not done, well, when it finishes, you have to call wake on the last waker that was passed to your poll method, otherwise you will never get polled again. It’s the futures responsibility to either wait on another future, or call wake.

Oh, looks like the executor only provides the guarantees of the most recently waker.

Im not sure, but I think I remember something from reading lots of docs, in any case you only have to call wake on the last one and discard the others, but memory is a bit fuzzy there.