Coroutines in Capy
You know how C++20 coroutines work at the language level. You understand threads, synchronization, and the problems that concurrency introduces. Now it is time to see how Capy brings these together into a practical, high-performance library.
Capy’s coroutine model is built around a single principle: asynchronous code should look like synchronous code. You write a function that reads from a socket, processes the data, and writes a response. The code reads top to bottom, with local variables and normal control flow. Capy handles suspension, resumption, thread scheduling, and cancellation behind the scenes. The result is code that is both easier to read and harder to get wrong.
But this is not magic, and it is not a black box. Every piece of Capy’s coroutine infrastructure is designed to be transparent. You can see how tasks are scheduled, control where they run, propagate cancellation, compose concurrent operations, and tune memory allocation. Understanding these mechanisms is what separates someone who uses the library from someone who uses it well.
What This Section Covers
-
The task Type — Declaring, returning values from, and awaiting
task<T>coroutines. -
Starting Coroutines — Starting coroutines with
run_async, and binding child tasks withrun. -
Executors and Execution Contexts — Executors, execution contexts, thread pools, and strands.
-
The IoAwaitable Protocol — How the executor and stop token propagate through a chain of awaited coroutines.
-
Stop Tokens and Cancellation — Cooperative cancellation with
std::stop_token, and how Capy tasks observe it. -
Concurrent Composition — Running tasks concurrently with
when_allandwhen_any. -
Frame Allocators — How coroutine frames are allocated, and how to customize the allocator.
-
Lambda Coroutine Captures — A critical pitfall: lambda captures versus coroutine frame lifetime.
Each topic builds on the last, and by the end you are writing real asynchronous programs with Capy.