The co_await Keyword
Understand the use of the 'co_await' keyword in C++20.
We'll cover the following...
co_await
eventually causes the execution of the coroutine to be suspended or resumed. The expression exp
in co_await exp
has to be a so-called awaitable expression, i.e. which must implement a specific interface, consisting of the three functions await_ready
, await_suspend
, and await_resume
.
A typical use case for co_await
is a server that waits for events.
Acceptor acceptor{443}while (true) {Socket socket = acceptor.accept(); // blockingauto request = socket.read(); // blockingauto response = handleRequest(request);socket.write(response); // blocking}
The server is quite simple because it sequentially answers each request in the same thread. The server listens on port (line 1), accepts the connection (line 3), reads the incoming data from the client (line 4), and writes its answer to the client (line 6). The calls in lines 3, 4, and 6 are blocking.
Thanks to co_await
, the blocking calls can now be suspended and resumed.
Acceptor acceptor{443}while (true) {Socket socket = co_await acceptor.accept();auto request = co_await socket.read();auto response = handleRequest(request);co_await socket.write(response);}
Before I present the challenging example of thread synchronization with coroutines, I want to start with something straightforward: starting 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: std::suspend_never()
.
#include <coroutine>#include <iostream>struct Job {struct promise_type;using handle_type = std::coroutine_handle<promise_type>;handle_type coro;Job(handle_type h): coro(h){}~Job() {if ( coro ) coro.destroy();}void start() {coro.resume();}struct promise_type {auto get_return_object() {return Job{handle_type::from_promise(*this)};}std::suspend_always initial_suspend() {std::cout << " Preparing job" << '\n'; return {};}std::suspend_always final_suspend() noexcept {std::cout << " Performing job" << '\n';return {};}void unhandled_exception() {}};};Job prepareJob() {co_await std::suspend_never();}int main() {std::cout << "Before job" << '\n';auto job = prepareJob();job.start();std::cout << "After job" << '\n';}
You may think that the coroutine prepareJob
(line 31) is meaningless because the Awaitable suspends always. No! The function prepareJob
is at least a coroutine factory using co_await
(line 32 ...