About desugar of expr.await

Hi guys,
From this comment , expr.await will desugar to the following code

match ::std::future::IntoFuture::into_future(<expr>) {
    mut __awaitee => loop {
        match unsafe { ::std::future::Future::poll(
            <::std::pin::Pin>::new_unchecked(&mut __awaitee),
            ::std::future::get_context(task_context),
        ) } {
            ::std::task::Poll::Ready(result) => break result,
            ::std::task::Poll::Pending => {}
        }
        task_context = yield ();    // A
    }
}

I notice that there is a yield at line A.
My question is to who will this line yield? And how this line is resumed? Many thanks!

It will yield to the runtime. It is resumed when the runtime decides that it wants to resume the future. Generally, you tell the runtime that you want to be resumed using the Waker in the task context. That is, the runtime creates a waker and gives it to the future in the context. When the future is ready to execute again, something calls wake on the waker, which lets the runtime know that it should resume the future soon.

Read more here.

1 Like

So the runtime will finally call std::ops::Generator::resume? Or it will call std::future::Future::poll again? I prefer std::ops::Generator::resume, because this behave the same way as a generator.

Generators are unstable, so the runtime cannot call any generator-specific methods. Therefore, the runtime calls Future::poll again. However, when the future comes from async/await syntax, the poll method just calls Generator::resume internally.

So an async function which has at least one await will return a future that implements both Future and Generator traits?

async fn test() {
  async {}.await;
}

No, it will not implement the generator trait. That's an internal implementation detail of the future. Instead, it will be a private internal type that you can't see that implements generator.

If this is true. How async runtime resume it? (You have said runtime choose not to call resume is just because generators is unstable, in my understanding, this implied that runtime can choose to use the resume approach)

Sorry, what I said was unprecise. Runtimes resume the future by calling poll. They cannot choose to use the generator api.

So, If using the poll approach, the yielded line will never be resumed?

No. Calling poll will resume the future from where it yielded.

I'll check that. Many thanks!!!

This used to work by converting the Generator produced by async to a Future with this method. This has recently changed in rust-lang/rust#104321 to be done internally in the compiler. I'm not sure how it works now, but it looks like it changes how yield are transformed into code at the MIR level by special casing the ones that come from async.

3 Likes

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.