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
相关产品推荐
相关产品推荐

