C++20 has three new keywords to make a coroutine out of a function:
co_await requires an Awaitable as arguments and starts the Awaiter workflow. Let me show in this post what that means.
To understand this post, you should have a basic understanding of coroutines. Here are my previous posts to coroutines, presenting coroutines from the practical perspective.
- Implementing Simple Futures With Coroutines
- Lazy Futures with Coroutines
- Executing a Future in a separate Thread with Coroutines
Before implementing Awaitables and showing their applications, I should write about the awaiter workflow.
The Awaiter Workflow
First, I have a short reminder. The awaiter workflow is based on the member functions of the Awaitable:
await_resume(). C++20 has the two predefined Awaitables
std::suspend_never, which I heavily used in this mini-series to coroutines.
Here is the awaiter workflow in prose.
The workflow is only executed if
false (line 1). In case it returns
true, the coroutine is ready and returns with the result of the call
awaitable.await_resume() (line 2).
Let me assume that
false. First, the coroutine is suspended (line 3), and the return value is immediately evaluated. The return type can be
void (line 4), a boolean (line 5), or another coroutine handle (line 6), such as
anotherCoroutineHandle. Depending on the return type, the program flow returns, or another coroutine is executed.
Let me apply the theory and start a job on request.
Starting a Job on Request
The coroutine in the following example is as simple as it can be. It awaits on the predefined Awaitable
You may think that the coroutine
prepareJob (line 1) is meaningless because the Awaitable always suspends. No! The function
prepareJob is at least a coroutine factory using
co_await (line 2) and returning a coroutine object. The function call
prepareJob() in line 3 creates the coroutine object of type
Job. When you study the data type Job, you recognize that the coroutine object is immediately suspended because the member function of the promise returns the Awaitable
std::suspend_always (line 5). This is precisely why the function call
job.start (line 5) is necessary to resume the coroutine (line 6). The member function
final_suspend() also returns
std::suspend_always (line 27).
startJob.cpp is an ideal starting point for further experiments. First, making the workflow transparent eases its understanding.
The Transparent Awaiter Workflow
I added a few comments to the previous program.
First, I replaced the predefined Awaitables
std::suspend_never with Awaitables
MySuspendAlways (line 1) and
MySuspendNever (line 2). I use them in lines 3, 4, and 5. The Awaitables mimic the behavior of the predefined Awaitables but additionally write a comment. Due to the use of
std::cout, the member functions
await_resume cannot be declared as
The screenshot of the program execution shows the control flow nicely, which you can observe on the Compiler Explorer.
initial_suspend (line 3) is executed at the beginning of the coroutine and the function
final_suspend at its end (line 4). The call
prepareJob() (line 6) triggers the creation of the coroutine object, and the function call
job.start() its resumption and, hence, completion (line 7). Consequently, the members
MySuspendAlways are executed. When you don’t resume the Awaitable, such as the coroutine object returned by the member function
final_suspend, the function
await_resume is not processed. In contrast, the Awaitable’s
MySuspendNever the function is immediately ready because
true and, hence, does not suspend.
In my next posts, I automatically resume the Awaiter on the same and, finally, on a separate thread.
Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, Rob North, Bhavith C Achar, and Marco Parri Empoli.
Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, Slavko Radman, and David Poole.
|My special thanks to Embarcadero|
|My special thanks to PVS-Studio|
|My special thanks to Tipi.build|
|My special thanks to Take Up Code|
I’m happy to give online seminars or face-to-face seminars worldwide. Please call me if you have any questions.
- Embedded Programmierung mit modernem C++ 12.12.2023 – 14.12.2023 (Präsenzschulung, Termingarantie)
Standard Seminars (English/German)
Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.
- C++ – The Core Language
- C++ – The Standard Library
- C++ – Compact
- C++11 and C++14
- Concurrency with Modern C++
- Design Pattern and Architectural Pattern with C++
- Embedded Programming with Modern C++
- Generic Programming (Templates) with C++
- Clean Code with Modern C++
- Phone: +49 7472 917441
- Mobil:: +49 176 5506 5086
- Mail: schulung@ModernesCpp.de
- German Seminar Page: www.ModernesCpp.de
- Mentoring Page: www.ModernesCpp.org
Modernes C++ Mentoring,