Cpp Coroutines

mohitmishra786/low-level-dev-skills/skills/low-level-programming/cpp-coroutines

作者 mohitmishra786bdc58472fa9f无许可证253 个星标收录于 2026年10月9日更新于 2026年10月9日仓库3个月前更新

C++20 coroutines skill for understanding coroutine mechanics and debugging. Use when working with co_await, co_yield, co_return, implementing promise_type, understanding coroutine frame layout, debugging suspended coroutines in GDB, or inspecting frame allocation with Compiler Explorer. Activates on queries about C++20 coroutines, co_await, co_yield, promise_type, coroutine_handle, coroutine suspension, or coroutine frame.

AI 生成的概览

指导智能体理解 C++20 协程机制、promise_type 实现、帧布局以及调试挂起的协程。

功能
该技能讲解 C++20 协程机制:co_await、co_yield 和 co_return 关键字、实现 promise_type、自定义 awaitable、协程帧内存布局以及帧分配开销。它提供最小 Task 类型、使用 co_yield 的 Generator、Boost.Asio co_spawn 用法和 C++23 std::generator 的参考代码,并给出在 GDB 中检查挂起协程的命令。它还列出常见陷阱和编译时间优化措施。
适用场景
适用于编写或审查 C++20 协程代码、实现 promise_type 或 awaitable,或在 GDB 中调试挂起的协程。也适合关于协程帧大小、堆分配消除或生成器模式的问题。
运行要求
不附带脚本或软件包,仅为说明和参考代码。运行示例需要 C++20 编译器,可选 GDB、Compiler Explorer,以及网络示例所需的 Boost.Asio。

C++20 Coroutines

Purpose

Guide agents through C++20 coroutine mechanics: co_await, co_yield, co_return, implementing the required promise_type, understanding coroutine frame memory layout, debugging suspended coroutines in GDB, and reducing frame allocation overhead.

Triggers

  • "How do co_await, co_yield, and co_return work?"
  • "How do I implement promise_type for a coroutine?"
  • "How does a coroutine suspend and resume?"
  • "How do I debug a suspended coroutine in GDB?"
  • "How much memory does a coroutine frame use?"
  • "How do I write a generator with co_yield?"

Workflow

1. The three coroutine keywords

cpp
// co_return — return a value and end the coroutineco_return value;
// co_yield — produce a value, suspend, resume laterco_yield value;
// co_await — suspend until an awaitable completesauto result = co_await some_awaitable;

A function is a coroutine if it contains any of these three keywords. Its return type must be a coroutine type with a promise_type.

2. Minimal coroutine type — Task

cpp
#include <coroutine>#include <stdexcept>#include <optional>
template <typename T>struct Task {    struct promise_type {        std::optional<T> value;        std::exception_ptr exception;
        Task get_return_object() {            return Task{std::coroutine_handle<promise_type>::from_promise(*this)};        }
        std::suspend_always initial_suspend() { return {}; }  // lazy start        std::suspend_always final_suspend() noexcept { return {}; }
        void return_value(T v) { value = std::move(v); }
        void unhandled_exception() { exception = std::current_exception(); }    };
    std::coroutine_handle<promise_type> handle;
    explicit Task(std::coroutine_handle<promise_type> h) : handle(h) {}
    Task(Task&&) = default;    Task& operator=(Task&&) = default;
    ~Task() { if (handle) handle.destroy(); }
    T get() {        handle.resume();                      // resume to completion        if (handle.promise().exception)            std::rethrow_exception(handle.promise().exception);        return std::move(*handle.promise().value);    }};
// UsageTask<int> compute() {    co_return 42;}
int main() {    auto task = compute();    int result = task.get();   // 42}

3. Generator with co_yield

cpp
template <typename T>struct Generator {    struct promise_type {        T current_value;
        Generator get_return_object() {            return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};        }
        std::suspend_always initial_suspend() { return {}; }        std::suspend_always final_suspend() noexcept { return {}; }        void return_void() {}        void unhandled_exception() { throw; }
        std::suspend_always yield_value(T value) {            current_value = value;            return {};                     // suspend after yielding        }    };
    std::coroutine_handle<promise_type> handle;
    explicit Generator(std::coroutine_handle<promise_type> h) : handle(h) {}    ~Generator() { if (handle) handle.destroy(); }
    struct iterator {        std::coroutine_handle<promise_type> handle;        bool done;
        iterator& operator++() {            handle.resume();            done = handle.done();            return *this;        }        T operator*() const { return handle.promise().current_value; }        bool operator!=(std::default_sentinel_t) const { return !done; }    };
    iterator begin() {        handle.resume();                   // advance to first yield        return {handle, handle.done()};    }    std::default_sentinel_t end() { return {}; }};
// UsageGenerator<int> iota(int start, int end) {    for (int i = start; i < end; ++i)        co_yield i;}
for (int x : iota(0, 5)) {    std::cout << x << ' ';   // 0 1 2 3 4}

4. Awaitable — custom co_await target

cpp
// An awaitable has three methods:// await_ready() — true means don't suspend// await_suspend(handle) — suspend: store handle, schedule resume// await_resume() — return value of co_await expression
struct TimerAwaitable {    int delay_ms;
    bool await_ready() const noexcept { return delay_ms <= 0; }
    void await_suspend(std::coroutine_handle<> h) {        // Schedule h.resume() to be called after delay        std::thread([h, this]() {            std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));            h.resume();        }).detach();    }
    void await_resume() const noexcept {}  // no return value};
// suspend_always and suspend_never are built-in awaitablesstd::suspend_always{};   // always suspendsstd::suspend_never{};    // never suspends (no-op)

5. Coroutine frame layout and memory

The compiler allocates a coroutine frame (heap object) containing:

  • Local variables that live across suspension points
  • The promise object
  • The current suspension state (where to resume)
  • A pointer to the resumption/destruction functions
cpp
// Inspect frame size with Compiler Explorer (godbolt.org)// Compile with: g++ -std=c++20 -O2 -S// Look for: operator new call size in the generated asm// Or: clang -std=c++20 -O2 -emit-llvm -S | grep "coro.size"
// Reduce frame size:// 1. Don't keep large objects alive across co_awaitstruct Bad {    std::vector<char> large_buf;   // whole vector lives in frame    co_return large_buf.size();    // large_buf crosses suspension};
// 2. Move data out before suspendingstd::vector<char> buf = get_data();size_t sz = buf.size();            // capture only what's neededbuf.clear();                       // release before suspensionco_await next_event;// sz still valid; buf released

6. Debugging suspended coroutines in GDB

bash
# Coroutines appear as regular stack frames after resume()# To inspect a suspended coroutine:
(gdb) info locals# Look for coroutine_handle variables
# Print the promise object(gdb) p *(promise_type*)(handle.__handle_)# GDB 14+ has coroutine-specific support(gdb) info coroutines        # GCC coroutine support (experimental)
# Step through coroutine execution(gdb) step     # enters co_await implementation(gdb) finish   # returns from coroutine frame function(gdb) next     # step over suspension point
# View all threads (coroutines running on thread pool)(gdb) info threads(gdb) thread 2(gdb) bt

7. Boost.Asio co_spawn and co_await

cpp
#include <boost/asio.hpp>#include <boost/asio/co_spawn.hpp>#include <boost/asio/awaitable.hpp>
namespace net = boost::asio;
net::awaitable<void> echo_session(net::ip::tcp::socket socket) {    char buf[1024];    for (;;) {        std::size_t n = co_await socket.async_read_some(net::buffer(buf));        co_await net::async_write(socket, net::buffer(buf, n));    }}
int main() {    net::io_context io;    net::co_spawn(io, listen_accept(io), net::detached);    io.run();}

co_spawn launches coroutines on an executor; co_await chains completion tokens without callback nesting.

8. std::generator (C++23)

cpp
#include <generator>#include <ranges>
std::generator<int> fibonacci() {    int a = 0, b = 1;    while (true) {        co_yield a;        auto next = a + b;        a = b;        b = next;    }}
// Usagefor (int v : fibonacci() | std::views::take(10))    printf("%d\n", v);

Lazy sequences without manual coroutine handle management — compiler provides std::generator promise type.

9. Coroutine frame layout in GDB

bash
# Compile with debug infog++ -std=c++20 -g -O0 -o app app.cppgdb ./app
gdb
(gdb) break my_coro(gdb) run(gdb) info frame                    # current stack frame(gdb) info locals                   # promise, handle in scope
# Inspect coroutine frame pointer (compiler-specific mangling)(gdb) p *(MyPromise*)h.address()    # h = coroutine_handle
# GCC coroutine support (GCC 14+)(gdb) info coroutines
# Pretty-print promise state(gdb) set print pretty on(gdb) p promise

Suspended coroutines may not appear on stack until resumed — trace via stored coroutine_handle.

10. Compilation time impact

Coroutines increase template instantiation and header parsing cost:

MitigationEffect
-O2 HALOReduces generated frame glue
Out-of-line co_await in .cppCuts recompilation cascade
Pimpl for coroutine return typesHides awaitable templates from headers
ccache / modulesSee skills/rust/rust-build-times patterns for C++

Measure with g++ -ftime-report or clang -ftime-trace. Coroutine-heavy headers (Asio) benefit from unity builds sparingly — balance with RAM use.

11. Common pitfalls

IssueCauseFix
co_await in a non-coroutineFunction missing coroutine return typeChange return type to a coroutine type
Dangling handle after co_returnUsing handle after coroutine finishesCheck handle.done() before resume
Double-resumeResuming an already-resumed coroutineTrack state; only resume when suspended
Coroutine frame never freedForgot handle.destroy()Use RAII wrapper (Task, Generator)
Heap allocation overheadNew frame per coroutine callEnable HALO (Heap Allocation eLision Optimization) with -O2
Recursive co_await depthStack overflow from deep chainsUse std::coroutine_handle<> tail-call pattern

Related skills

  • Use skills/compilers/cpp-templates for other advanced C++20 features
  • Use skills/rust/rust-async-internals for Rust's equivalent Future/Poll model
  • Use skills/debuggers/gdb for GDB session management

来源与署名

来源:mohitmishra786/low-level-dev-skills位于skills/low-level-programming/cpp-coroutines提交bdc5847

许可证: 无许可证

内容归原作者所有。SourceWeft 从公开仓库中收录这些内容。

举报或申请下架