You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++协程生成器调用next()第五次崩溃问题排查请求

协程生成器调用next()第5次触发SIGSEGV崩溃的问题分析与修复

问题现象

调用生成器的next()方法前4次可正常返回值,第5次调用时触发SIGSEGV段错误。期望协程执行到co_return时抛出异常而非崩溃。

崩溃调用栈

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=EXC_I386_GPFLT)
  * frame #0: 0x0000000104e4f076 Coroutines1`std::__1::coroutine_handle<Generator<int>::promise_type>::resume[abi:v160006](this=0x00007ff7bb0b3140) const at coroutine_handle.h:169:9
    frame #1: 0x0000000104e4e2a0 Coroutines1`Generator<int>::next(this=0x00007ff7bb0b3140) at main.cpp:46:18
    frame #2: 0x0000000104e4e0b9 Coroutines1`main at main.cpp:69:30
    frame #3: 0x00007ff803436366 dyld`start + 1942

复现代码

#include <iostream>
#include <coroutine>
#include <vector>

template<typename T>
struct Generator {
    struct promise_type;
    using handle_type = std::coroutine_handle<promise_type>;

    struct promise_type {
        T value;
        std::suspend_always yield_value(T val) {
            value = val;
            return {};
        }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        Generator get_return_object() { return Generator{handle_type::from_promise(*this)}; }
        void return_void() {}
        void unhandled_exception() { std::exit(1); }
    };

    handle_type coro;

    Generator(handle_type h): coro(h) {}
    ~Generator() {
        if (coro) {
            coro.destroy();
        }
    }
    Generator(const Generator&) = delete;
    Generator& operator=(const Generator&) = delete;
    Generator(Generator&& oth): coro(oth.coro) { oth.coro = nullptr; }
    Generator& operator=(Generator&& oth)  noexcept {
        if (this != &oth) {
            coro = oth.coro;
            oth.coro = nullptr;
        }
        return *this;
    }

    T next() {
        if (coro && !coro.done()) {
            coro.resume();
            if (!coro || coro.done())
                throw std::runtime_error("Generator has been fully consumed or does not exist.");
            return coro.promise().value;
        }
        throw std::runtime_error("Generator has been fully consumed or does not exist.");
    }
};

Generator<int> generator() {
    for (int i = 0; i < 5; ++i) {
        co_yield i;
    }
    co_return;
}

int main() {
    std::cout <<"Start!\n";
    auto gen = generator();

    while (true) {
        try {
            std::cout << gen.next() << '\n';
        }
        catch(std::runtime_error ex) {
            std::cout << ex.what() << "\n";
            break;
        }
    }
}

错误原因

1. final_suspend返回值错误

你的promise_type中final_suspend()返回std::suspend_never,这意味着协程执行到co_return时会立即销毁协程帧,此时coroutine_handle变成悬空句柄,后续对它的任何操作都会访问已释放的内存,触发段错误。

2. next()方法逻辑漏洞

第5次调用next()时,协程刚执行完最后一次co_yield,此时coro.done()返回false,代码会执行coro.resume()。但由于final_suspend不挂起,协程帧直接被销毁,coro变为无效句柄,后续判断coro.done()时会触发非法内存访问。

修复方案

步骤1:修改final_suspend返回std::suspend_always

让协程执行到结束时挂起,而非立即销毁,由我们主动控制协程帧的销毁时机:

std::suspend_always final_suspend() noexcept { return {}; }

步骤2:修正next()方法逻辑

调整判断顺序,避免访问无效句柄:

T next() {
    if (!coro || coro.done()) {
        throw std::runtime_error("Generator has been fully consumed or does not exist.");
    }
    coro.resume();
    if (coro.done()) {
        throw std::runtime_error("Generator has been fully consumed or does not exist.");
    }
    return coro.promise().value;
}

修复后执行流程

  • 前5次调用next():每次resume()后协程挂在co_yield,coro.done()为false,正常返回对应值。
  • 第6次调用next():resume()后协程执行到co_return,触发final_suspend挂起,此时coro.done()变为true,抛出异常,不会访问无效内存。

内容的提问来源于stack exchange,提问作者mike

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 08:35:58