cpp/language/coroutines

A coroutine is a function that can suspend execution to be resumed later. Coroutines are stackless: they suspend execution by returning to the caller and the data that is required to resume execution is stored separately from the stack. This allows for sequential code that executes asynchronously (e.g. to handle non-blocking I/O without explicit callbacks), and also supports algorithms on lazy-computed infinite sequences and other uses.

A function is a coroutine if its definition contains any of the following:
 * the expression — to suspend execution until resumed


 * the expression — to suspend execution returning a value


 * the statement — to complete execution returning a value

Every coroutine must have a return type that satisfies a number of requirements, noted below.

Restrictions
Coroutines cannot use, plain statements, or  ( or ).

,, s, s, and the cannot be coroutines.

Execution
Each coroutine is associated with
 * the promise object, manipulated from inside the coroutine. The coroutine submits its result or exception through this object.
 * the coroutine handle, manipulated from outside the coroutine. This is a non-owning handle used to resume execution of the coroutine or to destroy the coroutine frame.
 * the coroutine state, which is internal, dynamically-allocated storage (unless the allocation is optimized out), object that contains
 * the promise object
 * the parameters (all copied by value)
 * some representation of the current suspension point, so that a resume knows where to continue, and a destroy knows what local variables were in scope
 * local variables and temporaries whose lifetime spans the current suspension point.

When a coroutine begins execution, it performs the following:
 * allocates the coroutine state object using operator new.
 * copies all function parameters to the coroutine state: by-value parameters are moved or copied, by-reference parameters remain references (thus, may become dangling, if the coroutine is resumed after the lifetime of referred object ends — see below for examples).
 * calls the constructor for the promise object. If the promise type has a constructor that takes all coroutine parameters, that constructor is called, with post-copy coroutine arguments. Otherwise the default constructor is called.
 * calls and keeps the result in a local variable. The result of that call will be returned to the caller when the coroutine first suspends. Any exceptions thrown up to and including this step propagate back to the caller, not placed in the promise.
 * calls and s its result. Typical  types either return a std, for lazily-started coroutines, or std, for eagerly-started coroutines.
 * when resumes, starts executing the body of the coroutine.

Some examples of a parameter becoming dangling:

When a coroutine reaches a suspension point
 * the return object obtained earlier is returned to the caller/resumer, after implicit conversion to the return type of the coroutine, if necessary.

When a coroutine reaches the statement, it performs the following:
 * calls for
 * where has type
 * falling off the end of the coroutine. The behavior is undefined if the type has no  member function in this case.
 * falling off the end of the coroutine. The behavior is undefined if the type has no  member function in this case.


 * or calls for  where  has non-void type
 * destroys all variables with automatic storage duration in reverse order they were created.
 * calls and s the result.

If the coroutine ends with an uncaught exception, it performs the following:
 * catches the exception and calls from within the catch-block
 * calls and s the result (e.g. to resume a continuation or publish a result). It's undefined behavior to resume a coroutine from this point.

When the coroutine state is destroyed either because it terminated via or uncaught exception, or because it was destroyed via its handle, it does the following:


 * calls the destructor of the promise object.
 * calls the destructors of the function parameter copies.
 * calls operator delete to free the memory used by the coroutine state.
 * transfers execution back to the caller/resumer.

Dynamic allocation
Coroutine state is allocated dynamically via non-array operator new.

If the type defines a class-level replacement, it will be used, otherwise global operator new will be used.

If the type defines a placement form of operator new that takes additional parameters, and they match an argument list where the first argument is the size requested (of type std) and the rest are the coroutine function arguments, those arguments will be passed to operator new (this makes it possible to use  for coroutines).

The call to operator new can be optimized out (even if custom allocator is used) if
 * The lifetime of the coroutine state is strictly nested within the lifetime of the caller, and
 * the size of coroutine frame is known at the call site.

In that case, coroutine state is embedded in the caller's stack frame (if the caller is an ordinary function) or coroutine state (if the caller is a coroutine).

If allocation fails, the coroutine throws std, unless the type defines the member function. If that member function is defined, allocation uses the nothrow form of operator new and on allocation failure, the coroutine immediately returns the object obtained from to the caller, e.g.:

Promise
The type is determined by the compiler from the return type of the coroutine using std.

Formally, let and  denote the return type and parameter type list of a coroutine respectively,  and  (if any) denote the class type to which the coroutine belongs and its cv-qualification respectively if it is defined as a non-static member function, its  type is determined by:
 * , if the coroutine is not defined as a non-static member function,
 * , if the coroutine is defined as a non-static member function that is not rvalue-reference-qualified,
 * , if the coroutine is defined as a non-static member function that is rvalue-reference-qualified.

For example:

The unary operator suspends a coroutine and returns control to the caller. Its operand is an expression that either (1) is of a class type that defines a member or may be passed to a non-member, or (2) is convertible to such a class type by means of the current coroutine's.

A expression can only appear in a  expression within a regular, and cannot appear
 * in an ,
 * in a statement, unless it appears in an initializer of that declaration statement,
 * in the of an  (see, ,  and ), unless it appears in an initializer of that ,
 * in a, or
 * in the initializer of a block-scope variable with static or thread.

First, is converted to an awaitable as follows:
 * if is produced by an initial suspend point, a final suspend point, or a yield expression, the awaitable is, as-is.
 * otherwise, if the current coroutine's type has the member function, then the awaitable is.
 * otherwise, the awaitable is, as-is.

Then, the awaiter object is obtained, as follows:
 * if overload resolution for gives a single best overload, the awaiter is the result of that call:
 * for member overload,
 * for the non-member overload.

If the expression above is a, the awaiter object is a temporary from it. Otherwise, if the expression above is a, the awaiter object is the object to which it refers.
 * otherwise, if overload resolution finds no operator, the awaiter is awaitable, as-is.
 * otherwise, if overload resolution is ambiguous, the program is ill-formed.

Then, is called (this is a short-cut to avoid the cost of suspension if it's known that the result is ready or can be completed synchronously). If its result, contextually-converted to bool is then
 * The coroutine is suspended (its coroutine state is populated with local variables and current suspension point).
 * is called, where handle is the coroutine handle representing the current coroutine. Inside that function, the suspended coroutine state is observable via that handle, and it's this function's responsibility to schedule it to resume on some executor, or to be destroyed (returning false counts as scheduling)
 * if returns, control is immediately returned to the caller/resumer of the current coroutine (this coroutine remains suspended), otherwise
 * if returns ,
 * the value returns control to the caller/resumer of the current coroutine
 * the value resumes the current coroutine.
 * if returns a coroutine handle for some other coroutine, that handle is resumed (by a call to ) (note this may chain to eventually cause the current coroutine to resume).
 * if throws an exception, the exception is caught, the coroutine is resumed, and the exception is immediately re-thrown.

Finally, is called (whether the coroutine was suspended or not), and its result is the result of the whole  expression.

If the coroutine was suspended in the expression, and is later resumed, the resume point is immediately before the call to.

Note that because the coroutine is fully suspended before entering, that function is free to transfer the coroutine handle across threads, with no additional synchronization. For example, it can put it inside a callback, scheduled to run on a threadpool when async I/O operation completes. In that case, since the current coroutine may have been resumed and thus executed the awaiter object's destructor, all concurrently as continues its execution on the current thread,  should treat  as destroyed and not access it after the handle was published to other threads.

Example
Note: the awaiter object is part of coroutine state (as a temporary whose lifetime crosses a suspension point) and is destroyed before the expression finishes. It can be used to maintain per-operation state as required by some async I/O APIs without resorting to additional dynamic allocations.

The standard library defines two trivial awaitables: std and std.

expression returns a value to the caller and suspends the current coroutine: it is the common building block of resumable generator functions.

It is equivalent to

A typical generator's would store (copy/move or just store the address of, since the argument's lifetime crosses the suspension point inside the ) its argument into the generator object and return std, transferring control to the caller/resumer.

Library support
defines several types providing compile and run-time support for coroutines.