Files
boostlook/preview/2.cpp20-coroutines/2d.advanced.html
2026-02-23 20:00:04 -05:00

1000 lines
36 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=1">
<style>html.fonts-loading{visibility:hidden;opacity:0}</style>
<script>document.documentElement.classList.add('fonts-loading');</script>
<link rel="preload" href="../../_/font/NotoSansDisplay.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
<link rel="preload" href="../../_/font/NotoSansDisplay-Italic.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
<link rel="preload" href="../../_/font/MonaspaceNeon-Var.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
<link rel="preload" href="../../_/font/MonaspaceXenon-Var.woff2" as="font" type="font/woff2" crossorigin="anonymous" />
<script>
(function() {
'use strict';
var revealed = false;
var reveal = function() {
if (revealed) return;
revealed = true;
document.documentElement.classList.remove('fonts-loading');
};
setTimeout(reveal, 3000);
if (!('FontFace' in window) || !('fonts' in document)) {
setTimeout(reveal, 100);
return;
}
var uiRoot = '../../_';
var fonts = [
{
family: 'Noto Sans',
url: uiRoot + '/font/NotoSansDisplay.woff2',
descriptors: { style: 'normal', weight: '100 900', stretch: '62.5% 100%' }
},
{
family: 'Noto Sans',
url: uiRoot + '/font/NotoSansDisplay-Italic.woff2',
descriptors: { style: 'italic', weight: '100 900', stretch: '62.5% 100%' }
},
{
family: 'Monaspace Neon',
url: uiRoot + '/font/MonaspaceNeon-Var.woff2',
descriptors: { style: 'normal', weight: '400' }
},
{
family: 'Monaspace Xenon',
url: uiRoot + '/font/MonaspaceXenon-Var.woff2',
descriptors: { style: 'italic', weight: '400' }
}
];
var loadPromises = fonts.map(function(f) {
try {
var face = new FontFace(f.family, 'url("' + f.url + '")', f.descriptors);
return face.load().then(function(loaded) {
document.fonts.add(loaded);
return loaded;
}).catch(function() {
return null;
});
} catch (e) {
return Promise.resolve(null);
}
});
Promise.all(loadPromises)
.then(function() {
return document.fonts.ready;
})
.then(reveal)
.catch(reveal);
})();
</script> <title>Part IV: Advanced Topics :: Boost Libraries Documentation</title>
<link rel="canonical" href="https://antora.cppalliance.org/develop/lib/doc/capy/2.cpp20-coroutines/2d.advanced.html">
<link rel="prev" href="2c.machinery.html">
<link rel="next" href="../3.concurrency/3.intro.html">
<meta name="generator" content="Antora 3.1.14">
<link rel="stylesheet" href="../../_/css/boostlook.css">
<link rel="stylesheet" href="../../_/css/site.css">
<link rel="stylesheet" href="../../_/css/vendor/tabs.css">
<script>
(function() {
if (window.self !== window.top) return;
var theme = localStorage.getItem('antora-theme');
if (!theme && window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {
theme = 'dark';
}
if (theme === 'dark') document.documentElement.classList.add('dark');
})();
</script>
<script>var uiRootPath = '../../_'</script>
<link rel="icon" href="../../_/img/favicons/favicon.ico" type="image/x-icon">
<!-- Favicon configuration -->
<link rel="apple-touch-icon" sizes="180x180" href="../../_/img/favicons/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="../../_/img/favicons/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="../../_/img/favicons/favicon-16x16.png">
<link rel="manifest" href="../../_/img/favicons/site.webmanifest">
<link rel="shortcut icon" href="../../_/img/favicons/favicon.ico">
</head>
<body class="article toc2 toc-left">
<div class="boostlook">
<script type="module">import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.esm.min.mjs'; mermaid.initialize({"startOnLoad":true});</script> <div id="header">
<div id="toc" class="nav-container toc2" data-component="capy" data-version="">
<aside class="nav">
<button class="nav-close"></button>
<div class="panels">
<div class="nav-panel-menu is-active" data-panel="menu">
<nav class="nav-menu">
<div class="title-row">
<h3 class="title"><a href="../index.html">Boost.Capy</a></h3>
<button class="theme-toggle" aria-label="Toggle dark mode" title="Toggle theme" style="display:none">
<i class="fas fa-sun theme-icon-light"></i>
<i class="fas fa-moon theme-icon-dark"></i>
</button> </div>
<ul class="nav-list">
<ul class="nav-list">
<li class="" data-depth="1">
<a class="nav-link" href="../index.html">Introduction</a>
</li>
<li class="" data-depth="1">
<a class="nav-link" href="../why-capy.html">Why Capy?</a>
</li>
<li class="" data-depth="1">
<a class="nav-link" href="../quick-start.html">Quick Start</a>
</li>
<li class="" data-depth="1">
<a class="nav-link" href="2.intro.html">Introduction To C&#43;&#43;20 Coroutines</a>
</li>
<ul class="nav-list">
<li class="" data-depth="2">
<a class="nav-link" href="2a.foundations.html">Part I: Foundations</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="2b.syntax.html">Part II: C&#43;&#43;20 Syntax</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="2c.machinery.html">Part III: Coroutine Machinery</a>
</li>
<li class=" is-current-page" data-depth="2">
<a class="nav-link" href="2d.advanced.html">Part IV: Advanced Topics</a>
</li>
</ul>
<li class="" data-depth="1">
<a class="nav-link" href="../3.concurrency/3.intro.html">Introduction to Concurrency</a>
</li>
<ul class="nav-list">
<li class="" data-depth="2">
<a class="nav-link" href="../3.concurrency/3a.foundations.html">Part I: Foundations</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../3.concurrency/3b.synchronization.html">Part II: Synchronization</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../3.concurrency/3c.advanced.html">Part III: Advanced Primitives</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../3.concurrency/3d.patterns.html">Part IV: Communication &amp; Patterns</a>
</li>
</ul>
<li class="" data-depth="1">
<a class="nav-link" href="../4.coroutines/4.intro.html">Coroutines in Capy</a>
</li>
<ul class="nav-list">
<li class="" data-depth="2">
<a class="nav-link" href="../4.coroutines/4a.tasks.html">The task Type</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../4.coroutines/4b.launching.html">Launching Coroutines</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../4.coroutines/4c.executors.html">Executors and Execution Contexts</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../4.coroutines/4d.io-awaitable.html">The IoAwaitable Protocol</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../4.coroutines/4e.cancellation.html">Stop Tokens and Cancellation</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../4.coroutines/4f.composition.html">Concurrent Composition</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../4.coroutines/4g.allocators.html">Frame Allocators</a>
</li>
</ul>
<li class="" data-depth="1">
<a class="nav-link" href="../5.buffers/5.intro.html">Buffer Sequences</a>
</li>
<ul class="nav-list">
<li class="" data-depth="2">
<a class="nav-link" href="../5.buffers/5a.overview.html">Why Concepts, Not Spans</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../5.buffers/5b.types.html">Buffer Types</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../5.buffers/5c.sequences.html">Buffer Sequences</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../5.buffers/5d.system-io.html">System I/O Integration</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../5.buffers/5e.algorithms.html">Buffer Algorithms</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../5.buffers/5f.dynamic.html">Dynamic Buffers</a>
</li>
</ul>
<li class="" data-depth="1">
<a class="nav-link" href="../6.streams/6.intro.html">Stream Concepts</a>
</li>
<ul class="nav-list">
<li class="" data-depth="2">
<a class="nav-link" href="../6.streams/6a.overview.html">Overview</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../6.streams/6b.streams.html">Streams (Partial I/O)</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../6.streams/6c.sources-sinks.html">Sources and Sinks (Complete I/O)</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../6.streams/6d.buffer-concepts.html">Buffer Sources and Sinks</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../6.streams/6e.algorithms.html">Transfer Algorithms</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../6.streams/6f.isolation.html">Physical Isolation</a>
</li>
</ul>
<li class="" data-depth="1">
<a class="nav-link" href="../7.examples/7.intro.html">Example Programs</a>
</li>
<ul class="nav-list">
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7a.hello-task.html">Hello Task</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7b.producer-consumer.html">Producer-Consumer</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7c.buffer-composition.html">Buffer Composition</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7d.mock-stream-testing.html">Mock Stream Testing</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7e.type-erased-echo.html">Type-Erased Echo</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7f.timeout-cancellation.html">Timeout with Cancellation</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7g.parallel-fetch.html">Parallel Fetch</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7h.custom-dynamic-buffer.html">Custom Dynamic Buffer</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7i.echo-server-corosio.html">Echo Server with Corosio</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../7.examples/7j.stream-pipeline.html">Stream Pipeline</a>
</li>
</ul>
<li class="" data-depth="1">
<a class="nav-link" href="../8.design/8.intro.html">Design</a>
</li>
<ul class="nav-list">
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8a.CapyLayering.html">Layered Abstractions</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8b.Separation.html">Why Capy Is Separate</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8c.ReadStream.html">ReadStream</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8d.ReadSource.html">ReadSource</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8e.BufferSource.html">BufferSource</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8f.WriteStream.html">WriteStream</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8g.WriteSink.html">WriteSink</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8h.BufferSink.html">BufferSink</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8i.TypeEraseAwaitable.html">Type-Erasing Awaitables</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8j.any_buffer_sink.html">AnyBufferSink</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8k.Executor.html">Executor</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8l.RunApi.html">Run API</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8m.WhyNotCobalt.html">Why Not Cobalt?</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8n.WhyNotCobaltConcepts.html">Why Not Cobalt Concepts?</a>
</li>
<li class="" data-depth="2">
<a class="nav-link" href="../8.design/8o.WhyNotTMC.html">Why Not TooManyCooks?</a>
</li>
</ul>
<li class="" data-depth="1">
<a class="nav-link" href="../reference/boost/capy.html">Reference</a>
</li>
</ul>
</ul>
</nav>
</div>
</div>
</aside>
</div>
</div> <div id="content">
<article class="doc max-width-reset">
<div class="toolbar" role="navigation">
<button class="nav-toggle"></button>
<nav class="breadcrumbs" aria-label="breadcrumbs">
<ul>
<li>
<a href="../index.html" aria-label="Home: Boost.Capy">
<svg xmlns="http://www.w3.org/2000/svg" width="1rem" height="1rem" viewBox="0 -960 960 960" fill="#000000" aria-hidden="true"><path d="M160-120v-480l320-240 320 240v480H560v-280H400v280H160Z"/></svg>
</a>
</li>
<li><a href="2.intro.html">Introduction To C&#43;&#43;20 Coroutines</a></li>
<li><a href="2d.advanced.html">Part IV: Advanced Topics</a></li>
</ul>
</nav>
<div class="spirit-nav">
<a accesskey="p" href="2c.machinery.html">
<span class="material-symbols-outlined" title="Previous: Part III: Coroutine Machinery">arrow_back</span>
</a>
<a accesskey="u" href="2.intro.html">
<span class="material-symbols-outlined" title="Up: Introduction To C&#43;&#43;20 Coroutines">arrow_upward</span>
</a>
<a accesskey="n" href="../3.concurrency/3.intro.html">
<span class="material-symbols-outlined" title="Next: Introduction to Concurrency">arrow_forward</span>
</a>
</div></div>
<h1 class="page">Part IV: Advanced Topics</h1>
<div id="preamble">
<div class="sectionbody">
<div class="paragraph">
<p>This section covers advanced coroutine topics: symmetric transfer for efficient resumption, coroutine allocation strategies, and exception handling. These concepts are essential for building production-quality coroutine types.</p>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_prerequisites"><a class="anchor" href="#_prerequisites"></a>Prerequisites</h2>
<div class="sectionbody">
<div class="ulist">
<ul>
<li>
<p>Completed <a href="2c.machinery.html" class="xref page">Part III: Coroutine Machinery</a></p>
</li>
<li>
<p>Understanding of promise types, coroutine handles, and generators</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_symmetric_transfer"><a class="anchor" href="#_symmetric_transfer"></a>Symmetric Transfer</h2>
<div class="sectionbody">
<div class="paragraph">
<p>When a coroutine completes or awaits another coroutine, control must transfer somewhere. The naive approach—simply calling <code>handle.resume()</code>—has a problem: each nested coroutine adds a frame to the call stack. With deep nesting, you risk stack overflow.</p>
</div>
<div class="paragraph">
<p><strong>Symmetric transfer</strong> solves this by returning a coroutine handle from <code>await_suspend</code>. Instead of resuming the target coroutine via a function call, the compiler generates a tail call that transfers control without growing the stack.</p>
</div>
<div class="sect2">
<h3 id="_the_problem_stack_accumulation"><a class="anchor" href="#_the_problem_stack_accumulation"></a>The Problem: Stack Accumulation</h3>
<div class="paragraph">
<p>Consider a chain of coroutines where each awaits the next:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">task&lt;&gt; a() { co_await b(); }
task&lt;&gt; b() { co_await c(); }
task&lt;&gt; c() { co_return; }</code></pre>
</div>
</div>
<div class="paragraph">
<p>Without symmetric transfer, when <code>a</code> awaits <code>b</code>:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p><code>a</code> calls into the awaiter&#8217;s <code>await_suspend</code></p>
</li>
<li>
<p><code>await_suspend</code> calls <code>b.handle.resume()</code></p>
</li>
<li>
<p><code>b</code> runs, calls into its awaiter&#8217;s <code>await_suspend</code></p>
</li>
<li>
<p>That calls <code>c.handle.resume()</code></p>
</li>
<li>
<p>The stack now has frames for `a&#8217;s suspension, `b&#8217;s suspension, and `c&#8217;s execution</p>
</li>
</ol>
</div>
<div class="paragraph">
<p>Each suspension adds a stack frame. With thousands of nested coroutines, the stack overflows.</p>
</div>
</div>
<div class="sect2">
<h3 id="_the_solution_return_the_handle"><a class="anchor" href="#_the_solution_return_the_handle"></a>The Solution: Return the Handle</h3>
<div class="paragraph">
<p><code>await_suspend</code> can return a <code>std::coroutine_handle&lt;&gt;</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">std::coroutine_handle&lt;&gt; await_suspend(std::coroutine_handle&lt;&gt; h)
{
// store continuation for later
continuation_ = h;
// return handle to resume (instead of calling resume())
return next_coroutine_;
}</code></pre>
</div>
</div>
<div class="paragraph">
<p>When <code>await_suspend</code> returns a handle, the compiler generates code equivalent to:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">auto next = awaiter.await_suspend(current);
if (next != std::noop_coroutine())
next.resume(); // tail call, doesn't grow stack</code></pre>
</div>
</div>
<div class="paragraph">
<p>The key insight: returning a handle enables the compiler to implement the resumption as a tail call. The current stack frame is reused for the next coroutine.</p>
</div>
</div>
<div class="sect2">
<h3 id="_return_types_for_await_suspend"><a class="anchor" href="#_return_types_for_await_suspend"></a>Return Types for await_suspend</h3>
<div class="paragraph">
<p><code>await_suspend</code> can return three types:</p>
</div>
<div class="dlist">
<dl>
<dt class="hdlist1"><code>void</code></dt>
<dd>
<p>Always suspend. The coroutine is suspended and some external mechanism must resume it.</p>
</dd>
<dt class="hdlist1"><code>bool</code></dt>
<dd>
<p>Conditional suspension. Return <code>true</code> to suspend, <code>false</code> to continue without suspending.</p>
</dd>
<dt class="hdlist1"><code>std::coroutine_handle&lt;&gt;</code></dt>
<dd>
<p>Symmetric transfer. The returned handle is resumed; returning <code>std::noop_coroutine()</code> suspends without resuming anything.</p>
</dd>
</dl>
</div>
</div>
<div class="sect2">
<h3 id="_using_symmetric_transfer_in_generators"><a class="anchor" href="#_using_symmetric_transfer_in_generators"></a>Using Symmetric Transfer in Generators</h3>
<div class="paragraph">
<p>A production generator uses symmetric transfer at <code>final_suspend</code> to return to whoever is iterating:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">auto final_suspend() noexcept
{
struct awaiter
{
promise_type* p_;
bool await_ready() const noexcept { return false; }
std::coroutine_handle&lt;&gt; await_suspend(std::coroutine_handle&lt;&gt;) noexcept
{
// Return to the consumer that called resume()
return p_-&gt;consumer_handle_;
}
void await_resume() const noexcept {}
};
return awaiter{this};
}</code></pre>
</div>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_coroutine_allocation"><a class="anchor" href="#_coroutine_allocation"></a>Coroutine Allocation</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Every coroutine needs memory for its <strong>coroutine frame</strong>—the heap-allocated structure holding local variables, parameters, and suspension state.</p>
</div>
<div class="sect2">
<h3 id="_default_allocation"><a class="anchor" href="#_default_allocation"></a>Default Allocation</h3>
<div class="paragraph">
<p>By default, coroutines allocate their frames using <code>operator new</code>. The frame size depends on:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Local variables in the coroutine</p>
</li>
<li>
<p>Parameters (copied into the frame)</p>
</li>
<li>
<p>Promise type members</p>
</li>
<li>
<p>Compiler-generated bookkeeping</p>
</li>
</ul>
</div>
</div>
<div class="sect2">
<h3 id="_heap_allocation_elision_optimization_halo"><a class="anchor" href="#_heap_allocation_elision_optimization_halo"></a>Heap Allocation eLision Optimization (HALO)</h3>
<div class="paragraph">
<p>Compilers can sometimes eliminate coroutine frame allocation entirely through <strong>HALO</strong> (Heap Allocation eLision Optimization). When the compiler can prove that:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>The coroutine&#8217;s lifetime is contained within the caller&#8217;s lifetime</p>
</li>
<li>
<p>The frame size is known at compile time</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>&#8230;&#8203;it may allocate the frame on the caller&#8217;s stack instead of the heap.</p>
</div>
<div class="paragraph">
<p>HALO is most effective when:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Coroutines are awaited immediately after creation</p>
</li>
<li>
<p>The coroutine type is marked with <code><a id="clang::coro_await_elidable"></a></code> (Clang extension)</p>
</li>
<li>
<p>Optimization is enabled (<code>-O2</code> or higher)</p>
</li>
</ul>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">// HALO might apply here because the task is awaited immediately
co_await compute_something();
// HALO cannot apply here because the task escapes
auto task = compute_something();
store_for_later(std::move(task));</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_custom_allocators"><a class="anchor" href="#_custom_allocators"></a>Custom Allocators</h3>
<div class="paragraph">
<p>Promise types can customize allocation by providing <code>operator new</code> and <code>operator delete</code>:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">struct promise_type
{
// Custom allocation
static void* operator new(std::size_t size)
{
return my_allocator.allocate(size);
}
static void operator delete(void* ptr, std::size_t size)
{
my_allocator.deallocate(ptr, size);
}
// ... rest of promise type
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>The promise&#8217;s <code>operator new</code> receives only the frame size. To access allocator arguments passed to the coroutine, use the leading allocator convention with <code>std::allocator_arg_t</code> as the first parameter.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_exception_handling"><a class="anchor" href="#_exception_handling"></a>Exception Handling</h2>
<div class="sectionbody">
<div class="paragraph">
<p>Exceptions in coroutines require special handling because a coroutine can suspend and resume across different call stacks.</p>
</div>
<div class="sect2">
<h3 id="_the_exception_flow"><a class="anchor" href="#_the_exception_flow"></a>The Exception Flow</h3>
<div class="paragraph">
<p>When an exception is thrown inside a coroutine and not caught:</p>
</div>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>The exception is caught by an implicit try-catch surrounding the coroutine body</p>
</li>
<li>
<p><code>promise.unhandled_exception()</code> is called while the exception is active</p>
</li>
<li>
<p>After <code>unhandled_exception()</code> returns, <code>co_await promise.final_suspend()</code> executes</p>
</li>
<li>
<p>The coroutine completes (suspended or destroyed, depending on <code>final_suspend</code>)</p>
</li>
</ol>
</div>
</div>
<div class="sect2">
<h3 id="_options_in_unhandled_exception"><a class="anchor" href="#_options_in_unhandled_exception"></a>Options in unhandled_exception()</h3>
<div class="paragraph">
<p><strong>Terminate the program:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">void unhandled_exception()
{
std::terminate();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Store for later retrieval:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">void unhandled_exception()
{
exception_ = std::current_exception();
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Rethrow immediately:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">void unhandled_exception()
{
throw; // propagates to whoever resumed the coroutine
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Swallow the exception:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">void unhandled_exception()
{
// silently ignored - almost always a mistake
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_the_store_and_rethrow_pattern"><a class="anchor" href="#_the_store_and_rethrow_pattern"></a>The Store-and-Rethrow Pattern</h3>
<div class="paragraph">
<p>For tasks and generators where callers expect results, store the exception and rethrow it when results are requested:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">struct promise_type
{
std::exception_ptr exception_;
void unhandled_exception()
{
exception_ = std::current_exception();
}
};
// In the return object's result accessor:
T get_result()
{
if (handle_.promise().exception_)
std::rethrow_exception(handle_.promise().exception_);
return std::move(handle_.promise().result_);
}</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_exception_example"><a class="anchor" href="#_exception_example"></a>Exception Example</h3>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">#include &lt;coroutine&gt;
#include &lt;exception&gt;
#include &lt;iostream&gt;
#include &lt;stdexcept&gt;
struct Task
{
struct promise_type
{
std::exception_ptr exception;
Task get_return_object()
{
return Task{std::coroutine_handle&lt;promise_type&gt;::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void return_void() {}
void unhandled_exception()
{
exception = std::current_exception();
}
};
std::coroutine_handle&lt;promise_type&gt; handle;
Task(std::coroutine_handle&lt;promise_type&gt; h) : handle(h) {}
~Task() { if (handle) handle.destroy(); }
void run() { handle.resume(); }
void check_exception()
{
if (handle.promise().exception)
std::rethrow_exception(handle.promise().exception);
}
};
Task risky_operation()
{
std::cout &lt;&lt; "Starting risky operation" &lt;&lt; std::endl;
throw std::runtime_error("Something went wrong");
co_return; // never reached
}
int main()
{
Task task = risky_operation();
try
{
task.run();
task.check_exception();
std::cout &lt;&lt; "Operation completed successfully" &lt;&lt; std::endl;
}
catch (std::exception const&amp; e)
{
std::cout &lt;&lt; "Operation failed: " &lt;&lt; e.what() &lt;&lt; std::endl;
}
}</code></pre>
</div>
</div>
<div class="paragraph">
<p><strong>Output:</strong></p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-asciidoc hljs" data-lang="asciidoc">Starting risky operation
Operation failed: Something went wrong</code></pre>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_initialization_exceptions"><a class="anchor" href="#_initialization_exceptions"></a>Initialization Exceptions</h3>
<div class="paragraph">
<p>Exceptions thrown before the first suspension point (before <code>initial_suspend</code> completes) propagate directly to the caller without going through <code>unhandled_exception()</code>. If <code>initial_suspend()</code> returns <code>suspend_always</code>, the coroutine suspends before any user code runs, avoiding this edge case.</p>
</div>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_building_a_production_generator"><a class="anchor" href="#_building_a_production_generator"></a>Building a Production Generator</h2>
<div class="sectionbody">
<div class="paragraph">
<p>With all these concepts, here is a production-quality generic generator:</p>
</div>
<div class="listingblock">
<div class="content">
<pre class="highlightjs highlight"><code class="language-cpp hljs" data-lang="cpp">#include &lt;coroutine&gt;
#include &lt;exception&gt;
#include &lt;utility&gt;
template&lt;typename T&gt;
class Generator
{
public:
struct promise_type
{
T value_;
std::exception_ptr exception_;
Generator get_return_object()
{
return Generator{Handle::from_promise(*this)};
}
std::suspend_always initial_suspend() noexcept { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
std::suspend_always yield_value(T v)
{
value_ = std::move(v);
return {};
}
void return_void() noexcept {}
void unhandled_exception()
{
exception_ = std::current_exception();
}
// Prevent co_await inside generators
template&lt;typename U&gt;
std::suspend_never await_transform(U&amp;&amp;) = delete;
};
using Handle = std::coroutine_handle&lt;promise_type&gt;;
class iterator
{
Handle handle_;
public:
using iterator_category = std::input_iterator_tag;
using value_type = T;
using difference_type = std::ptrdiff_t;
iterator() : handle_(nullptr) {}
explicit iterator(Handle h) : handle_(h) {}
iterator&amp; operator++()
{
handle_.resume();
if (handle_.done())
{
auto&amp; promise = handle_.promise();
handle_ = nullptr;
if (promise.exception_)
std::rethrow_exception(promise.exception_);
}
return *this;
}
T&amp; operator*() const { return handle_.promise().value_; }
bool operator==(iterator const&amp; other) const
{
return handle_ == other.handle_;
}
};
iterator begin()
{
if (handle_)
{
handle_.resume();
if (handle_.done())
{
auto&amp; promise = handle_.promise();
if (promise.exception_)
std::rethrow_exception(promise.exception_);
return iterator{};
}
}
return iterator{handle_};
}
iterator end() { return iterator{}; }
~Generator() { if (handle_) handle_.destroy(); }
Generator(Generator const&amp;) = delete;
Generator&amp; operator=(Generator const&amp;) = delete;
Generator(Generator&amp;&amp; other) noexcept
: handle_(std::exchange(other.handle_, nullptr)) {}
Generator&amp; operator=(Generator&amp;&amp; other) noexcept
{
if (this != &amp;other)
{
if (handle_) handle_.destroy();
handle_ = std::exchange(other.handle_, nullptr);
}
return *this;
}
private:
Handle handle_;
explicit Generator(Handle h) : handle_(h) {}
};</code></pre>
</div>
</div>
<div class="paragraph">
<p>This generator:</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Provides a standard iterator interface for range-based for loops</p>
</li>
<li>
<p>Stores and rethrows exceptions during iteration</p>
</li>
<li>
<p>Prevents <code>co_await</code> inside generators via deleted <code>await_transform</code></p>
</li>
<li>
<p>Manages coroutine lifetime with RAII</p>
</li>
<li>
<p>Supports move semantics</p>
</li>
</ul>
</div>
</div>
</div>
<div class="sect1">
<h2 id="_conclusion"><a class="anchor" href="#_conclusion"></a>Conclusion</h2>
<div class="sectionbody">
<div class="paragraph">
<p>You have now learned the complete mechanics of C&#43;&#43;20 coroutines:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong>Keywords</strong><code>co_await</code>, <code>co_yield</code>, and <code>co_return</code> transform functions into coroutines</p>
</li>
<li>
<p><strong>Promise types</strong> — Control coroutine behavior at initialization, suspension, completion, and error handling</p>
</li>
<li>
<p><strong>Coroutine handles</strong> — Lightweight references for resuming, querying, and destroying coroutines</p>
</li>
<li>
<p><strong>Symmetric transfer</strong> — Efficient control flow without stack accumulation</p>
</li>
<li>
<p><strong>Allocation</strong> — Custom allocation and HALO optimization</p>
</li>
<li>
<p><strong>Exception handling</strong> — Capturing and propagating exceptions across suspension points</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>These fundamentals prepare you for understanding Capy&#8217;s <code>task&lt;T&gt;</code> type and the IoAwaitable protocol, which build on standard coroutine machinery with executor affinity and stop token propagation.</p>
</div>
</div>
</div>
<div class="edit-this-page">
<a href="https://github.com/cppalliance/capy/edit/develop/doc/modules/ROOT/pages/2.cpp20-coroutines/2d.advanced.adoc">Edit this Page</a>
</div>
<nav class="pagination">
<span class="prev"><a href="2c.machinery.html">Part III: Coroutine Machinery</a></span>
<span class="next"><a href="../3.concurrency/3.intro.html">Introduction to Concurrency</a></span>
</nav>
</article>
</div>
<div id="footer">
<script id="site-script" src="../../_/js/site.js" data-ui-root-path="../../_"></script>
<script async src="../../_/js/vendor/highlight.js"></script>
<script async src="../../_/js/vendor/tabs.js" data-sync-storage-key="preferred-tab"></script>
</div>
</div>
</body>
</html>