Search code examples
c++memory-managementc++20unique-ptrc++-coroutine

Why does std::coroutine_handle only refer to a coroutine (via raw pointer) instead of owning it (via std::unique_ptr)?


I'm interested in understanding the reason behind the design choice of having std::coroutine_handle be effectively a non-owner of the coroutine state/frame.

I understand that (at least usually?) the return type of the coroutine is responsible for storing the std::coroutine_handle, and is therefore responsible for the lifetime of the coroutine state/frame.

But still, what would the disvantage have been if std::coroutine_handle handled the coroutine state/frame via std::unique_ptr (which at runtime is as performant as a raw pointer, no?)?

Is that the copiability of std::coroutine_handle around is so valuable?


Solution

  • The non-owning handle is the one that makes sense as part of the low-level coroutine API. await_suspend is passed the handle to the coroutine that is awaiting, but that coroutine will typically be actually owned by the return object (like std::generator, the proposed std::task, and so on). So at least one of those two handles has to be non-owning. So if the language is to provide only one coroutine handle type, it has to be the non-owning one.

    Plus, you can easily create an owning coroutine handle type that wraps the non-owning one. But in practice, this tends to not be that useful, because it's expected that users would rarely need to interact with coroutine handles directly. You should only need to write a small number of coroutine return types and it's hopefully not too much of a burden to remember to write a destructor and move constructor (and possibly move assignment operator) for them.