Coroutines in CPP
Coroutines in CPP
https://godbolt.org/
Routine == Instruction[]
Routine:
- An ordered group of instructions
Instruction:
- Abstraction of machine behavior.
- Transition between machine states
Invocation
Jump(goto) to the start of the routine
Activation
Jump into a point of the routine
Suspension
Jump to another routine’s point without finalization
Finalization
Destruction(Clean up) of routine state (reaching the end of epilogue)
Subroutine
A routine that supports 2 operations: Invoke/Finalize
Routine
(Caller)
Subroutine
Invoke
Finalize
Subroutine
A routine that supports Invoke/Finalize
Finalize (Return)
Invoke (Call)
Process
The way to run a program over the O/S.
A set of routines
Thread
The abstraction of a control flow in a process
Processor (CPU)
Coroutine
“Subroutines are special case of … coroutines” – Donald Knuth
Routine
(Caller)
Coroutine
Invoke
Suspend
Routine
(Resumer) Resume
Finalize
Operating Programming Machine
System Language
Instruction
API Type Architecture
System
Process
Coroutine
Thread
Subroutine
Function
Fiber
Operating Programming Machine
System Language
Instruction
API
Conceptually, Type Architecture
Coroutine is irrelevant withSystem
the thread.
Process
Coroutine
Thread
Subroutine
Function
Fiber
http://www.tortall.net/projects/yasm/manual/ru/html/objfmt-win64-
exception.html
Call Stack
A way to manage function frame.
In the C language,
all functions are subroutine !
https://manybutfinite.com/post/journey-to-the-stack/
Coroutine
◦ Routine that holds its state (Function Frame)
◦ 4 Operations from its definition
Task Class
◦ An organized data structure with its member functions
Stackless Coroutine
◦ Allocate coroutine’s frame in free store (dynamically allocate)
Invoke No change
Finalize co_return
Suspend co_await, co_yield // unary operator
Resume coro.resume() // coroutine_handle<P>::resume()
At a glance…
How can we define the C++ Coroutine?
If one of the following exists in the function’s body…
◦ co_await expression
◦ co_yield expression
◦ co_return statement
◦ for co_await statement
How can we compile the C++ Coroutine?
MSVC
◦ Visual Studio 2015 or later vcxproj property > C/C++
◦ /await
Clang Family
◦ 5.0 or later
◦ -fcoroutines-ts –stdlib=libc++ -std=c++2a
GCC
◦ Not yet …
C3783: ‘main’ cannot be a coroutine
#include <experimental/coroutine>
auto my_first_coroutine() {
co_await std::experimental::suspend_never{};
}
promise_type ??
Coroutine Promise Requirement
A special type for compiler
https://isocpp.org/files/papers/N4402.pdf
https://lewissbaker.github.io/2018/09/05/understanding-the-promise-type
Coroutine Promise Requirement (N4402)
Expression Note
P{} Promise must be default constructible
p.get_return_object() The return value of funtion. It can be future<T>, or some user-defined type.
p.return_value(v) co_return statement. Pass the value v and the value will be consumed later.
co_return statement. Pass void. Can be invoked when the coroutine returns.
p.return_value()
And calling this can be thought as “No more value”.
Pass the exception.
p.set_exception(e)
It will throw when the resumer activates the function with this context.
p.yield_value(v) co_yield expression. Similar to return_value(v).
p.initial_suspend() If return true, suspends at initial suspend point.
p.final_suspend() If return true, suspends at final suspend point.
https://isocpp.org/files/papers/N4402.pdf
https://luncliff.github.io/posts/Exploring-MSVC-Coroutine.html
Coroutine Promise Requirement (N4402)
Expression Note
P{} Promise must be default constructible
p.get_return_object() The return value of funtion. It can be future<T>, or some user-defined type.
p.return_value(v) co_return statement. Pass the value v and the value will be consumed later.
co_return statement. Pass void. Can be invoked when the coroutine returns.
p.return_value()
And calling this can be thought as “No more value”.
Pass the exception.
p.set_exception(e)
It will throw when the resumer activates the function with this context.
Programmer have to fill out the functions
p.yield_value(v) co_yield expression. Similar to return_value(v).
p.initial_suspend() If return true, suspends at initial suspend point.
p.final_suspend() If return true, suspends at final suspend point.
https://isocpp.org/files/papers/N4402.pdf
https://luncliff.github.io/posts/Exploring-MSVC-Coroutine.html
We will cover that later…
The point is that…
Coroutine(stack-less) frame is managed by Type System(promise_type)
Awaitable Type and co_await Operator
How can we suspend the coroutine?
#include <iostream>
Expected output?
#include <iostream>
https://wandbox.org/permlink/fRebS2VGQHRdGepp
#include <iostream>
Routine Coroutine
(Caller)
Invoke
Suspend
Leaked?
Or intended?
#include <iostream>
https://wandbox.org/permlink/PoX9rQzx0u1rTAx6
#include <experimental/coroutine> Coroutine: suspend and wait for resume
#include <future>
co_await expression
using namespace std::experimental;
using awaitable = suspend_always;
if (aw.await_ready() == false) {
auto rh = coroutine_handle<promise_type>::from_promise(*p);
aw.await_suspend(rh);
// ... return ...
}
__suspend_point_n:
aw.await_resume();
}
if (aw.await_ready() == false) {
auto rh = coroutine_handle<promise_type>::from_promise(*p);
aw.await_suspend(rh);
// ... return ...
}
__suspend_point_n:
aw.await_resume();
}
if (aw.await_ready() == false) {
auto rh = coroutine_handle<promise_type>::from_promise(*p);
aw.await_suspend(rh);
// ... return ...
}
__suspend_point_n:
aw.await_resume();
}
await_suspend &
coroutine_handle<P>
using namespace std::experimental;
using awaitable = suspend_always;
if (aw.await_ready() == false) {
auto rh = coroutine_handle<promise_type>::from_promise(*p);
aw.await_suspend(rh);
// ... return ...
}
__suspend_point_n:
aw.await_resume(); Receiver function for the frame
}
await_suspend &
coroutine_handle<P>
// <experimental/coroutine> // namespace std::experimental
class suspend_never
{
public:
bool await_ready() {
return true;
}
void await_suspend(coroutine_handle<void>){}
void await_resume(){}
};
class suspend_always
{
public:
bool await_ready() {
return false;
}
void await_suspend(coroutine_handle<void>){}
void await_resume(){}
};
Pre-defined Awaitable Types
class suspend_never
{
public:
bool await_ready() {
return true;
}
void await_suspend(coroutine_handle<void>){}
void await_resume(){}
};
await_ready() == true
class suspend_never
{
public:
bool await_ready() {
return true;
}
void await_suspend(coroutine_handle<void>){}
void await_resume(){} If true, bypass to await_resume
};
await_ready() == false
class suspend_always
{
public:
bool await_ready() {
return false;
}
void await_suspend(coroutine_handle<void>){}
void await_resume(){} If false, call await_suspend
}; and yield control flow to activator routine
auto routine_with_await(awaitable& aw) -> return_ignore
{
if (aw.await_ready() == false) {
auto rh = coroutine_handle<promise_type>::from_promise(*p);
aw.await_suspend(rh);
// ... Return ...
}
__suspend_point_n:
aw.await_resume();
}
Ready – Suspend - Resume
struct wait_for_tuple
{
bool await_ready();
void await_suspend(coroutine_handle<void>);
auto await_resume() -> std::tuple<int, bool>;
};
Nothing special…
struct wait_for_tuple
{
bool await_ready();
void await_suspend(coroutine_handle<void>);
auto await_resume() -> std::tuple<int, bool>;
};
await_resume()
using namespace std::experimental;
using awaitable = suspend_always;
Programmer’s Intent
Syntactic Sugar
co_await expression
Awaitable Type’s Role
It’s an interface for co_await
◦ Operator co_await requires multiple function
◦ await_ready
◦ await_suspend
◦ await_resume
By using co_await…
◦ Compiler can generates suspend point at the line.
◦ Programmer can manage coroutine’s control flow with the suspension
Coroutine Promise Requirement (N4736)
What is Promise Type? What should be in it?
using namespace std::experimental;
using awaitable = suspend_always;
if (aw.await_ready() == false) {
auto rh = coroutine_handle<promise_type>::from_promise(*p);
aw.await_suspend(rh);
// ... return ...
}
__suspend_point_n:
aw.await_resume();
}
…promise_type?
Promise Type’s Role
Allow compile time check (Type System)
◦ coroutine_traits<T...>
co_await coro::suspend_never{};
}
co_await coro::suspend_never{};
}
co_await coro::suspend_never{};
}
What is this template class?
template <class>
struct __void_t { typedef void type; };
Ignore SFINAE …
template <class>
struct __void_t { typedef void type; };
The core
#include <experimental/coroutine>
namespace coro = std::experimental;
co_await coro::suspend_never{};
} Does the return_type has promise_type?
coroutine_traits<T...>
Return Type Extension using coroutine_traits<>
Even though return_type doesn’t contain promise_type,
Programmer can provide template specialization of coroutine_traits<T...>
to use the type as a return type of C++ coroutine
co_await coro::suspend_never{};
}
#include <experimental/coroutine>
auto my_first_coroutine() {
co_await std::experimental::suspend_never{};
}
If there is a coroutine…
using namespace std::experimental;
auto example(int a, double b, char *c) -> return_type
{
using T = coroutine_traits<return_type, int, double, char *>;
using promise_type = T::promise_type;
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try {
// ... programmer's code ...
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
Code Generation from Promise
using namespace std::experimental;
auto example(int a, double b, char *c) -> return_type
{
using T = coroutine_traits<return_type, int, double, char *>;
using promise_type = T::promise_type;
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try {
// ... programmer's code ...
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
Where is my code?
using namespace std::experimental;
auto example(int a, double b, char *c) -> return_type
{
using T = coroutine_traits<return_type, int, double, char *>;
using promise_type = T::promise_type;
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try {
// ... programmer's code ...
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
}
co_await p.final_suspend();
Most of operations come from
promise_type
using namespace std::experimental;
auto example(int a, double b, char *c) -> return_type
{
using T = coroutine_traits<return_type, int, double, char *>;
using promise_type = T::promise_type;
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try { promise-constructor-arguments
// ... programmer's code ...
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
Promise: Construction
using namespace std::experimental;
auto example() -> return_type
{
using T = coroutine_traits<return_type>;
using promise_type = T::promise_type;
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try { For non-matching argument,
// ... programmer's code ... use default constructor
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
Promise: Construction
#include <experimental/coroutine>
struct return_sample
{
For general case
struct promise_type
{
promise_type();
~promise_type();
Promise: Ctor/Dtor
using namespace std::experimental;
auto example() -> return_type
{
using T = coroutine_traits<return_type>;
using promise_type = T::promise_type;
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try {
// ... programmer's code ...
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
Code generation from Promise
using namespace std::experimental;
auto example() -> return_type
{
using T = coroutine_traits<return_type>;
using promise_type = T::promise_type;
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try {
// ... programmer's code ...
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
Code generation from Promise
using return_type = return_sample;
auto example() -> return_type
{
// return_type * __return_object = ...
promise_type p{};
*__return_object = { p.get_return_object() };
} Promise: return object
struct return_sample
{
struct promise_type
{
auto get_return_object() -> promise_type*
{
return this;
}
static promise_type *get_return_object_on_allocation_failure() noexcept;
};
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try {
// ... programmer's code ...
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
What about the exception handling?
using namespace std::experimental;
auto example() -> return_type
{
using T = coroutine_traits<return_type>;
using promise_type = T::promise_type;
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try {
// ... programmer's code ...
The last exception handler by the compiler
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
using namespace std::experimental;
auto example() -> return_type struct return_sample
{ {
using T = coroutine_traits<return_type>; struct promise_type
using promise_type = T::promise_type; {
void unhandled_exception()
// return_type * __return_object = ... {
promise_type p{}; // std::current_exception();
std::terminate();
*__return_object = { p.get_return_object() }; }
co_await p.initial_suspend(); };
try { };
// ... programmer's code ...
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
Promise: Unhandled Exception
using namespace std::experimental;
auto example() -> return_type
{
using T = coroutine_traits<return_type>;
using promise_type = T::promise_type;
*__return_object = { p.get_return_object() };
co_await p.initial_suspend();
try {
// ... programmer's code ...
}
catch (...) {
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
}
Promise: initial/final suspend
using namespace std::experimental;
auto example() -> return_type struct return_sample
{ {
using T = coroutine_traits<return_type>; struct promise_type
using promise_type = T::promise_type; {
auto initial_suspend()
// return_type * __return_object = ... {
promise_type p{}; return suspend_never{};
}
*__return_object = { p.get_return_object() }; auto final_suspend()
co_await p.initial_suspend(); {
try { return suspend_never{};
// ... programmer's code ... }
} };
catch (...) { };
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
} Expect Awaitable Return Type
using namespace std::experimental;
auto example() -> return_type struct return_sample
{ {
using T = coroutine_traits<return_type>; struct promise_type
using promise_type = T::promise_type; {
auto initial_suspend()
// return_type * __return_object = ... {
promise_type p{}; return suspend_never{};
}
*__return_object = { p.get_return_object() }; auto final_suspend()
co_await p.initial_suspend(); {
try { return suspend_never{};
// ... programmer's code ... }
} };
catch (...) { };
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
} Initial Suspend
Enter programmer’s code immediately?
using namespace std::experimental;
auto example() -> return_type struct return_sample
{ {
using T = coroutine_traits<return_type>; struct promise_type
using promise_type = T::promise_type; {
auto initial_suspend()
// return_type * __return_object = ... {
promise_type p{}; return suspend_never{};
}
*__return_object = { p.get_return_object() }; auto final_suspend()
co_await p.initial_suspend(); {
try { return suspend_never{};
// ... programmer's code ... }
} };
catch (...) { };
p.unhandled_exception();
}
__final_suspend_point:
co_await p.final_suspend();
} Final Suspend
Delete coroutine frame after co_return?
#include <experimental/coroutine>
using namespace std::experimental;
+ with Promise
#include <experimental/coroutine> struct promise_type
using namespace std::experimental; {
Item* ptr = nullptr;
auto example() -> pack<int> {
co_await suspend_never{}; suspend_never initial_suspend(){ return{}; }
co_return 0; suspend_never final_suspend(){ return{}; }
} auto get_return_object() {
return this;
template <typename Item>
}
struct pack
// for co_return with value
{
void return_value(Item& ref) {
promise_type* prom;
ptr = std::addressof(ref);
}
pack(promise_type* p) :prom{ p } {};
};
auto get() -> Item& {
Item* ptr = prom->ptr;
return *ptr;
}
co_return requires
};
Just co_return ?
#include <experimental/coroutine> struct promise_type
using namespace std::experimental; {
Item* ptr = nullptr;
auto example() -> pack<int> {
co_await suspend_never{}; suspend_never initial_suspend(){ return{}; }
co_return; suspend_never final_suspend(){ return{}; }
} auto get_return_object() {
return this;
}
// for empty co_return
void return_void() {}
};
co_return requires
return_void for empty return
#include <experimental/coroutine> struct promise_type
using namespace std::experimental; {
Item* ptr = nullptr;
auto example() -> pack<int> {
co_await suspend_never{}; suspend_never initial_suspend(){ return{}; }
} suspend_never final_suspend(){ return{}; }
auto get_return_object() {
return this;
}
try {
co_return 0; // programmer's code
}
catch (...) {
p->unhandled_exception();
}
__final_suspend_point:
co_await p->final_suspend();
__destroy_point:
delete p;
}
try {
int _t1 = 0;
p->return_value(_t1);
goto __final_suspend_point;
}
catch (...) {
p->unhandled_exception(); Generated code from co_return 0;
}
__final_suspend_point:
co_await p->final_suspend();
__destroy_point:
delete p;
}
Promise Type’s Role
Allow compile time check (Type System)
◦ coroutine_traits<T...>
Task Class
◦ An organized data structure with its member functions
template <>
class coroutine_handle<void>
{
protected:
prefix_t prefix;
static_assert(sizeof(prefix_t) == sizeof(void*));
public:
operator bool() const;
void resume();
void destroy();
bool done() const;
public:
using coroutine_handle<void>::coroutine_handle;
public:
auto promise() -> promise_type&;
static coroutine_handle from_promise(promise_type& prom);
};
<experimental/resumable> in VC++
github.com/llvm-mirror/libcxx/release_70/include/experimental/coroutine Promise-Aware handle
bool operator==(const coroutine_handle<void>, const coroutine_handle<void>);
bool operator!=(const coroutine_handle<void>, const coroutine_handle<void>);
bool operator< (const coroutine_handle<void>, const coroutine_handle<void>);
bool operator> (const coroutine_handle<void>, const coroutine_handle<void>);
bool operator<=(const coroutine_handle<void>, const coroutine_handle<void>);
bool operator>=(const coroutine_handle<void>, const coroutine_handle<void>);
<experimental/resumable> in VC++
github.com/llvm-mirror/libcxx/release_70/include/experimental/coroutine And some helpers
C++ Coroutine over Type System
Programmer guides the compiler using types
◦ Promise Type
◦ Awaitable Type
Routine
(Caller)
Coroutine
Invoke
Suspend
Routine
(Resumer) Resume How can we jump into
the position?
Finalize
template <typename PromiseType = void>
class coroutine_handle;
template <>
class coroutine_handle<void>
{
protected:
prefix_t prefix; Compiler specific memory layout
static_assert(sizeof(prefix_t) == sizeof(void*));
public:
operator bool() const;
void resume();
void destroy(); Compiler Intrinsic
bool done() const;
MSVC Clang
<experimental/resumable> in VC++
github.com/llvm-mirror/libcxx/release_70/include/experimental/coroutine
https://clang.llvm.org/docs/LanguageExtensions.html#c-coroutines-support-builtins
Coroutine Intrinsic: MSVC
explicit operator bool() const {
return _Ptr != nullptr;
}
void destroy(){
_coro_destroy(_Ptr);
}
void resume() {
__builtin_coro_resume(__handle_);
}
void destroy() {
__builtin_coro_destroy(__handle_);
}
template <>
class coroutine_handle<void>
{
protected:
prefix_t prefix;
static_assert(sizeof(prefix_t) == sizeof(void*));
public:
operator bool() const;
void resume();
What about this?
void destroy();
bool done() const;
CppCon 2016: “Introduction to C++ Coroutines” What are in the Coroutine Frame?
Same with subroutine
◦ Local variables
◦ Function arguments
◦ Temporary variables (+ Awaitable)
◦ Return value
◦ Coroutine Frame’s Prefix (for coroutine_handle<void>)
◦ Promise object
◦ Compiler specific storage (maybe)
2018/n4736.pdf
class return_type {
public:
struct promise_type {
auto operator new(size_t sz) -> void *;
void operator delete(void *ptr, size_t sz);
};
};
auto example(Args... args) -> return_type {
using T = coroutine_traits<return_type, Args...>;
using promise_type = T::promise_type;
using frame_type = tuple<frame_prefix, promise_type, Args...>;
__destroy_point:
promise_type::operator delete(frame, sizeof(frame_type));
}
__destroy_point:
promise_type::operator delete(frame, sizeof(frame_type));
}
__destroy_point:
promise_type::operator delete(frame, sizeof(frame_type));
}
__destroy_point:
promise_type::operator delete(frame, sizeof(frame_type));
}
__destroy_point:
promise_type::operator delete(frame, sizeof(frame_type));
}
N4736, 15.8.3
in a coroutine, a copy of a coroutine parameter can be omitted and references to that
copy replaced with references to the corresponding parameters if the meaning of the
program will be unchanged …
2018/n4736.pdf
auto example(Args... args) -> return_type {
using T = coroutine_traits<return_type, Args...>;
using promise_type = T::promise_type;
using frame_type = tuple<frame_prefix, promise_type, Args...>;
Frame Prefix?
template <>
struct coroutine_handle<void> {
struct _Resumable_frame_prefix {
typedef void(__cdecl *_Resume_fn)(void *);
_Resume_fn _Fn;
uint16_t _Index;
uint16_t _Flags;
};
protected:
_Resumable_frame_prefix *_Ptr = nullptr;
};
<experimental/resumable>
template <>
struct coroutine_handle<void> {
struct _Resumable_frame_prefix {
typedef void(__cdecl *_Resume_fn)(void *);
_Resume_fn _Fn;
uint16_t _Index;
uint16_t _Flags;
};
protected:
_Resumable_frame_prefix *_Ptr = nullptr; switch (frame->index) {
}; case 2: // initial suspended
goto __suspend_point_1;
case 4: // suspended in point_1
goto __suspend_point_2;
A-Ha !
// the other case ...
<experimental/resumable>
Calling Convention: __cdecl
Stack clean-up by caller(calling function)
== For void return, no clean-up is required
== coroutine’s frame is never touched by caller after _Resume_fn.
https://docs.microsoft.com/ko-kr/cpp/cpp/cdecl?view=vs-2017
template <>
struct coroutine_handle<void> {
struct _Resumable_frame_prefix {
typedef void(__cdecl *_Resume_fn)(void *);
_Resume_fn _Fn;
uint16_t _Index;
uint16_t _Flags;
};
protected:
_Resumable_frame_prefix *_Ptr = nullptr;
};
So this is almost equivalent to goto
<experimental/resumable>
template <>
class coroutine_handle<void> {
private:
template <class _PromiseT> friend class coroutine_handle;
void* __handle_;
};
Zero information :(
libcxx/release_70/include/experimental/coroutine#L252
static const size_t _ALIGN_REQ = sizeof(void *) * 2;
static const size_t _ALIGNED_SIZE =
is_empty_v<_PromiseT>
? 0
: ((sizeof(_PromiseT) + _ALIGN_REQ - 1) & ~(_ALIGN_REQ - 1));
<experimental/resumable> In VC++…
static const size_t _ALIGN_REQ = sizeof(void *) * 2;
static const size_t _ALIGNED_SIZE =
is_empty_v<_PromiseT>
? 0
: ((sizeof(_PromiseT) + _ALIGN_REQ - 1) & ~(_ALIGN_REQ - 1));
__alignof(_PromiseT)
Clang’s Frame
o void*
Simple conversion
o Awaitable Type
From suspend operation(await_suspend)
promise_type &_prom;
coroutine_handle<promise_type>
promise_type &
coroutine_handle<void>
void *
Their Role
Coroutine Frame
Handle Awaitable
from_address/address await_suspend
done z resume await_resume
await_ready
from_promise/promise destroy
Promise
new/ctor delete/dtor
get_return_object
initial_suspend
final_suspend
return_void yield_value
return_value await_transform
Their Relationship
Coroutine Frame
Promise
new/ctor
Awaitable
Promise
new/ctor
get_return_object
initial_suspend
Operation:
Invoke
Coroutine Frame
Handle Awaitable
done z
Promise
final_suspend
return_void
return_value Operation:
Return
Coroutine Frame
Handle
destroy
Promise
delete/dtor
Handle Awaitable
await_suspend
done z
await_ready
Promise
initial_suspend
final_suspend
yield_value
await_transform Operation:
Suspend
Coroutine Frame
Handle Awaitable
z resume await_resume
await_ready
Operation:
Resume
Coroutine Frame
Handle Awaitable
from_address/address await_suspend
z
from_promise/promise
Promise
Access to Handle
Coroutine Frame
Handle Awaitable
from_address/address await_suspend
done z resume await_resume
await_ready
from_promise/promise destroy
Promise
new/ctor delete/dtor
get_return_object
initial_suspend
final_suspend
return_void yield_value
return_value await_transform
All in One
Thank You!
For question & error, please send mail to [email protected]
Coroutine Generator
Understanding co_yield
co_yield Operator
Similar to co_return, but the name implies suspension rather than return
auto subroutine(uint32_t sum = 0) -> uint32_t
{
for (uint32_t v : example())
sum += v;
return sum;
}
uint32_t item{};
co_yield item = 1;
}
return sum;
}
return sum;
}
p.yield_value(item);
co_await suspend_always{}; // this is not return!
}
<experimental/generator> in VC++
auto subroutine(uint32_t sum = 0) -> uint32_t
{
for (uint32_t v : example())
sum += v;
return sum;
}
Generator: Usage
auto subroutine(uint32_t sum = 0) -> uint32_t
{
{
auto g = example();
auto it = g.begin();
auto e = g.end();
for (; it != e; ++it)
{
auto v = *it;
sum += v;
} Just that of input iterator
} (directional iteration)
// g is destroyed
return sum;
}
~generator(){
if (_Coro)
_Coro.destroy();
}
private:
coroutine_handle<promise_type> _Coro = nullptr;
};
iterator() = default;
iterator(nullptr_t) : _Coro(nullptr){}
iterator(coroutine_handle<promise_type> _CoroArg) : _Coro(_CoroArg){}
};
};
Generator: Iterator
template <typename _Ty, typename _Alloc = allocator<char>>
struct generator
{
struct iterator {
using iterator_category = input_iterator_tag;
iterator &operator++(){
_Coro.resume();
if (_Coro.done())
_Coro = nullptr;
return *this;
}
};
_NODISCARD iterator begin(){
if (_Coro) {
_Coro.resume();
if (_Coro.done()) return {nullptr};
}
return {_Coro};
} Advance ==
};
_NODISCARD iterator end(){ return {nullptr}; }
Resume Operation
template <typename _Ty, typename _Alloc = allocator<char>>
struct generator
{
struct promise_type {
_Ty const *_CurrentValue;
promise_type &get_return_object(){
return *this;
}
bool initial_suspend(){ return (true); }
Just address calculation
bool final_suspend(){ return (true); }
void yield_value(_Ty const &_Value){
_CurrentValue = _STD addressof(_Value);
}
};
};
coroutine_handle<promise_type> _Coro = nullptr;
Generator: Promise
Is this type really safe?
auto current_threads() -> generator<DWORD>
{
auto pid = GetCurrentProcessId();
CloseHandle(snapshot);
}
CloseHandle(snapshot);
}
auto current_threads() -> generator<DWORD>
{
auto pid = GetCurrentProcessId();
https://wandbox.org/permlink/6FGKZjuzjNYoSmI1
https://godbolt.org/z/M4atrm Let’s assume there is a queue…
auto program(coro_queue& fq, coro_queue& bq) -> return_ignore;
void main_subroutine()
{
auto fg = make_queue(); // for foreground
auto bg = make_queue(); // for background
auto repeat = 3;
while (repeat--)
{
co_await foreground; void print_thread_id(const char* label)
print_thread_id("front"); {
cout << label
co_await background; << "\t" << this_thread::get_id()
print_thread_id("back"); << endl;
} }
print_thread_id("return");
co_return;
}
Our coroutine
auto program(coro_queue& foreground, //
coro_queue& background) -> return_ignore
{
using namespace std;
print_thread_id("invoke");
Expression:
auto repeat = 3; Function selects its thread
while (repeat--)
{
co_await foreground;
print_thread_id("front");
co_await background;
print_thread_id("back");
}
print_thread_id("return");
co_return;
}
Semantics:
Send a handle through Message Queue
auto program(coro_queue& fq, coro_queue& bq) -> return_ignore;
void coro_worker(coro_queue* q)
q); // worker thread function
{
auto coro = coroutine_handle<void>{};
auto repeat = 10;
PopNext:
if (q->try_pop(coro) == false)
std::this_thread::sleep_for(10ms);
else
{
if (coro.done())
coro.destroy();
else
coro.resume();
}
if (repeat--) // for some condition ...
goto PopNext; // continue
}
void coro_worker(coro_queue* q)
{
auto coro = coroutine_handle<void>{};
auto repeat = 10;
PopNext:
if (q->try_pop(coro) == false)
std::this_thread::sleep_for(10ms);
else
{
if (coro.done())
coro.destroy();
else
coro.resume();
}
if (repeat--) // for some condition ...
goto PopNext; // continue
}
https://godbolt.org/z/EnNBrL
https://godbolt.org/z/eCVc6I Can we use bool for co_await ?
struct return_ignore; // ... we already covered this type ...
https://godbolt.org/z/EnNBrL
https://godbolt.org/z/eCVc6I
struct return_ignore;
struct return_ignore {
struct promise_type {
// ...
auto await_transform(bool cond) {
// return an awaitable
// that is came from its argument
return suspend_with_condition{cond};
}
}; If there is await_transform,
// ... it is applied before co_await operator
};
struct return_ignore;
struct return_ignore {
struct promise_type {
// ...
auto await_transform(bool cond) {
// return an awaitable
// that is came from its argument
return suspend_with_condition{cond};
}
};
// ...
};