C++协程通过promise类型传递值时触发段错误求助
C++协程段错误排查
问题描述
我实现了一个C++协程,通过三次co_await操作接收调用者使用返回类型Parser的feed()函数传入的新迭代器。这些值存储在promise类型中,并在Awaitable::resume()时传递给协程体内的引用迭代器(刚接触协程,如有更优实现方式欢迎指出)。但当前代码触发了段错误,相关代码如下:
#include <cstdio> #include <coroutine> #include <string_view> struct Promise; struct Parser : public std::coroutine_handle<Promise> { using promise_type = ::Promise; auto feed(std::convertible_to<std::string_view> auto&& view); ~Parser() { destroy(); } }; struct Awaitable { Promise* promise; std::string_view::iterator& start_; std::string_view::iterator& end_; Awaitable(std::string_view::iterator& start, std::string_view::iterator& end) : start_{ start } , end_{ end } { } auto await_ready() -> bool { printf("await_ready!\n"); return true; } auto await_suspend(std::coroutine_handle<Promise> handle) { promise = &handle.promise(); printf("await_resume!\n"); } auto await_resume() -> void; }; struct Promise { std::pair<std::string_view::iterator, std::string_view::iterator> iterpair; auto get_return_object() -> Parser { return {Parser::from_promise(*this)}; }; auto initial_suspend() noexcept -> std::suspend_never { return {}; } auto final_suspend() noexcept -> std::suspend_always { return {}; } auto await_transform(std::pair<std::string_view::iterator, std::string_view::iterator> iterpair) -> Awaitable { return {iterpair.first, iterpair.second}; } auto return_void() { printf("return void!\n"); } auto unhandled_exception() {} }; auto Awaitable::await_resume() -> void { printf("await_resume!\n"); start_ = promise->iterpair.first; end_ = promise->iterpair.second; } auto Parser::feed(std::convertible_to<std::string_view> auto&& view) { auto my_view = std::string_view{ view }; promise().iterpair = { my_view.begin(), my_view.end() }; } auto coro() -> Parser { std::string_view::iterator start, end; // feed1 co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end }; // feed2 co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end }; // feed3 co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end }; co_return; } int main() { std::string_view part1 = "Hello sir!"; std::string_view part2 = "How is the world doing?"; std::string_view part3 = "Blazingly good sir!"; Parser myparser = coro(); myparser.feed(part1); myparser.feed(part2); myparser.feed(part3); }
段错误原因
1. 野指针访问直接触发崩溃
Awaitable::await_ready()返回true,根据C++协程规则,此时await_suspend()不会被执行,直接进入await_resume()。但Awaitable::promise指针仅在await_suspend()中被赋值,此时它是未初始化的野指针。在await_resume()中访问promise->iterpair时,野指针解引用直接触发段错误。
2. 协程执行逻辑完全偏离设计意图
你希望协程挂起等待外部feed()传入数据,但:
Promise::initial_suspend()返回std::suspend_never,导致协程创建后立即开始执行;Awaitable::await_ready()返回true,导致每次co_await都不会挂起协程。
最终协程会一口气执行完所有三次co_await和co_return,完全没有等待外部输入的过程,同时触发野指针错误。
修复建议
- 修改
Awaitable::await_ready()返回false,确保await_suspend()被调用,正确初始化promise指针; - 修改
Promise::initial_suspend()返回std::suspend_always,让协程创建后先挂起,等待第一次feed()调用时再唤醒; - 在
Parser::feed()中添加resume()调用,设置完数据后唤醒协程处理输入; - 优化析构逻辑,避免销毁未完成的协程帧。
修复后的示例代码:
#include <cstdio> #include <coroutine> #include <string_view> struct Promise; struct Parser : public std::coroutine_handle<Promise> { using promise_type = ::Promise; auto feed(std::convertible_to<std::string_view> auto&& view); ~Parser() { if (done()) { destroy(); } } }; struct Awaitable { Promise* promise; std::string_view::iterator& start_; std::string_view::iterator& end_; Awaitable(std::string_view::iterator& start, std::string_view::iterator& end) : start_{ start } , end_{ end } { } auto await_ready() -> bool { printf("await_ready!\n"); return false; } auto await_suspend(std::coroutine_handle<Promise> handle) { promise = &handle.promise(); printf("await_suspend!\n"); return handle; } auto await_resume() -> void; }; struct Promise { std::pair<std::string_view::iterator, std::string_view::iterator> iterpair; auto get_return_object() -> Parser { return {Parser::from_promise(*this)}; }; auto initial_suspend() noexcept -> std::suspend_always { return {}; } auto final_suspend() noexcept -> std::suspend_always { return {}; } auto await_transform(std::pair<std::string_view::iterator, std::string_view::iterator> iterpair) -> Awaitable { return {iterpair.first, iterpair.second}; } auto return_void() { printf("return void!\n"); } auto unhandled_exception() {} }; auto Awaitable::await_resume() -> void { printf("await_resume!\n"); start_ = promise->iterpair.first; end_ = promise->iterpair.second; printf("Received data: %.*s\n", static_cast<int>(end_ - start_), &*start_); } auto Parser::feed(std::convertible_to<std::string_view> auto&& view) { auto my_view = std::string_view{ view }; promise().iterpair = { my_view.begin(), my_view.end() }; resume(); } auto coro() -> Parser { std::string_view::iterator start, end; co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end }; co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end }; co_await std::pair<std::string_view::iterator, std::string_view::iterator>{ start, end }; co_return; } int main() { std::string_view part1 = "Hello sir!"; std::string_view part2 = "How is the world doing?"; std::string_view part3 = "Blazingly good sir!"; Parser myparser = coro(); myparser.feed(part1); myparser.feed(part2); myparser.feed(part3); }
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

