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

How coroutine_handle<Promise>::from_promise() works in C++


I read that coroutine_handle can be reconstructed from a reference to the coroutine's promise object using coroutine_handle<Promise>::from_promise() function. To my knowledge, the promise object is defined like this:

class resumable{
public:
  struct promise_type;
  bool resume();
};

struct resumable::promise_type{
  resumable get_return_object() {/**/}
  auto initial_suspend() {/**/}
  auto final_suspend() {/**/}
  void return_void() {}
  void unhandled_exception();
};

I am failing to figure out how reconstruction could work given the fact that promise_type doesn't hold any reference to the coroutine handle. Just the opposite, the coroutine handle stores the reference to the promise object, which can be returned by the promise() method of the handle. I can't find any information on what actually happens in from_promise function.


Solution

  • It works by fiat. That is, it works because the standard says that it works, and implementations must therefore find a way to implement coroutines in such a way that it is possible.

    When creating a coroutine, the implementation creates two things: the coroutine_handle (or rather, the guts of it to which the coroutine_handle points) and the promise object. The location of both of these things is controlled entirely by the compiler. So, the compiler could very easily allocate them contiguously with each other, such that a coroutine's stack would essentially start with a struct {coroutine_guts handle; Promise promise};, where coroutine_guts is the object that the coroutine_handle points to.

    Given that knowledge, you know that the handle for any promise type lives sizeof(coroutine_guts) bytes before any promise object's address (alignment requirements of the Promise type can adjust this, but such things can be queried from the type). And since from_promise takes a promise object, you can just offset the pointer to get the coroutine_guts and shove that in a coroutine_handle<Promise>.

    Now, that is just one way of doing it; an implementation doesn't have to do it this way. What matters is that the implementation has control over where the promise object lives relative to the coroutine internal data. Or more specifically, the promise lives inside of that internal data. Regardless of how you look at it, the compiler knows everything it needs to in order to convert the address of a promise into the internal data needed to fill in a coroutine_handle.