Technical Background
Stackless
C++20 coroutines are stackless, meaning they don’t have their own stack.
A stack in C++ describes the callstack, i.e. all the function frames stacked. A function frame is the memory a function needs to operate, i.e. a slice of memory to store its variables and information such as the return address.
The size of a function frame is known at compile time, but not outside the compile unit containing its definition. |
int bar() {return 0;} // the deepest point of the stack
int foo() {return bar();}
int main()
{
return bar();
}
The call stack in the above example is:
main()
foo()
bar()
Coroutines can be implemented a stackful, which means that it allocates a fixes chunk of memory and stacks function frames similar to a thread. C++20 coroutines are stackless, i.e. they only allocate their own frame and use the callers stack on resumption. Using our previous example:
fictional_eager_coro_type<int> example()
{
co_yield 0;
co_yield 1;
}
void nested_resume(fictional_eager_coro_type<int>& f)
{
f.resume();
}
int main()
{
auto f = example();
nested_resume(f);
f.reenter();
return 0;
}
This will yield a call stack similar to this:
main()
f$example()
nested_resume()
f$example()
f$example()
The same applies if a coroutine gets moved accross threads.
Lazy & eager
Coroutines are lazy if they only start execution of its code after it gets resumed, while an eager one will execute right-away until its first suspension point (i.e. a co_await
, co_yield
or co_return
expression.)
lazy_coro co_example()
{
printf("Entered coro\n");
co_yield 0;
printf("Coro done\n");
}
int main()
{
printf("enter main\n");
auto lazy = co_example();
printf("constructed coro\n");
lazy.resume();
printf("resumed once\n");
lazy.resume();
printf("resumed twice\n");
return 0;
}
Which will produce output like this:
enter main
constructed coro
Entered coro
resumed once
Coro Done
resumed twice
Whereas an eager coro would look like this:
eager_coro co_example()
{
printf("Entered coro\n");
co_yield 0;
printf("Coro done\n");
}
int main()
{
printf("enter main\n");
auto lazy = co_example();
printf("constructed coro\n");
lazy.resume();
printf("resumed once\n");
return 0;
}
Which will produce output like this:
enter main
Entered coro
constructed coro
resume once
Coro Done