Search code examples
c++c++20c++-coroutine

The state of a coroutine after final-suspend


A coroutine has an initial suspend point and a final suspend point. A coroutine_handle is done when it is suspended at the final suspend point. However, this suspension is done via a co_await promise.final_suspend(). And this function can return std::never_suspend or a similar awaitable.

So... what happens then? coroutine_handle::resume yields UB if the coroutine is suspended at the final suspend point, but what happens if you're past the final suspend point?

Presumably, the coroutine is not considered suspended, so resume is UB by that rule. But what can you do with a handle to a coroutine that is past the final suspend point?

Or is there never any point to having your final_suspend return anything other than std::suspend_always?


Solution

  • Coroutine functions are defined by transformations on what the body of the corotuine is. After the main body of the coroutine finishes and exits (either via co_return or running off the end of the block), the following code happens:

    co_await p.final_suspend();
    //destruct promise p
    //destruct parameters in coroutine frame
    //destroy coroutine state
    

    Where p is the promise object for the coroutine. So if final_suspend doesn't cause a suspension with co_await, the promise is destroyed, followed by the parameters of the coroutine frame, followed by the coroutine state object itself.

    coroutine_handle references a coroutine state. If that has been destroyed, then the handle does not refer to a suspended coroutine (since there is no coroutine). And resume therefore will have undefined behavior. done similarly has UB if the handle is not suspended (because it isn't a handle).