C++ Coroutine simple tutorial

  • 2020-05-27 06:41:02
  • OfStack

In C++, a function is an coroutine if its body implementation contains any keyword from co_await, co_yield, co_return. Among them:

co_await: suspends the current coroutine
co_return: returns 1 result from the current coroutine
co_yield: returns 1 result and suspends the current coroutine

An coroutine must be recognized and compiled at compile time, and in some cases must be de-specialized. Let's take a simple coroutine and say how the C++ compiler modifies the coroutine.


//  We assume that the template function is 1 a coroutine
template <typename TRet, typename  ...  TArgs>
TRet func(TArgs args ... )
{
 body; // body At least it does co_await , co_yield , co_return3 of 1 . 
}

This function will be changed by the compiler as follows:


//  It will be expanded at compile time as follows 
template <typename TRet, typename ... TArgs>
TRet func(TArgs args...)
{
 using promise_t = typename coroutine_traits<TRet, TArgs...>::promise_type;

 promise_t promise;
 auto __return__ = promise.get_return_object(); //  this __return__ It will be handled by the compiler 

 co_await promise.initial_suspend();

 try
 {   // co_return expr; => promise.return_value(expr); goto final_suspend;
  body; // co_return;  => promise.return_void(); goto final_suspend;
 }   // co_yield expr; => co_await promise.yield_value(expr);
 catch (...)
 {
  promise.set_exception(std::current_exception());
 }

final_suspend:
 co_await promise.final_suspend();
}

The above is the basic form of an coroutine. In fact, the key to an coroutine is the promise associated with it.
Another concept associated with coroutine promise is called awaitable. Awaitable can be called a waitable object. One awaitable object needs to implement three related functions:

1. await_ready: whether the awaitable instance has ready

2, await_suspend: suspend awaitable. This function passes in a parameter of type coroutine_handle. This is a variable generated by the compiler. The suspend procedure can specify when, where and how the coroutine is resume. For example, when implementing suspend function, put coroutine_handle into threadpool. The current coroutine then runs in the background thread assigned by the thread pool.

3. await_resume: this function is called when coroutine is rerun.

So there are three ways to make a type awaitable:

1. If the code related to this type cannot be modified, it shall be implemented as follows:

bool await_ready(T & );
void await_suspend(T & , coroutine_handle < promise_type > );
auto await_resume(T & ); auto depends on the situation

2. When the code related to this type can be modified, three member functions need to be added:

bool await_ready();
void await_suspend(coroutine_handle < promise_type > ch);
auto await_resume();

3. Implement operator co_await operator, return one type of proxy that can wait, and implement the above three functions.


Related articles: