为何`final_suspend`先于`await_suspend`调用?同步协程问题咨询
协程同步返回时final_suspend先于await_suspend执行的问题
现象说明
原本代码正常执行时,输出如下,最终返回42:
0x600002aa01c0 initial_suspend 0x6000031a02a0 initial_suspend 0x6000031a02a0 await_ready 0x6000031a02a0 await_suspend (precursor = 0x600002aa01c0) 0x6000031a02a0 final_suspend 0x6000031a02a0 await_resume 0x600002aa01c0 final_suspend Result: 42
但注释掉bar()函数中的co_await next_tick()行(使bar变为无任何co_await调用的同步返回函数)后,输出变为:
0x6000008d81c0 initial_suspend 0x6000013d82a0 initial_suspend 0x6000013d82a0 final_suspend 0x6000013d82a0 await_ready 0x6000013d82a0 await_suspend (precursor = 0x6000008d81c0) Result: 0
可见此时final_suspend先于await_suspend被调用,导致负责恢复父协程的precursor变量未被正确赋值,父协程无法恢复,最终结果错误。
疑问与需求
- 这种行为是C++20协程的预期行为吗?
- 该如何修复这个问题,让同步返回的协程也能正确触发父协程的恢复?
- 额外问题:
coroutine_handle无法直接检测底层promise是否还存在,导致无法在await_suspend内轻松判断当前协程是否已经进入final_suspend阶段。
相关代码
#include <coroutine> #include <functional> #include <iostream> #include <list> std::list<std::function<void (void)>> g_queue; struct next_tick { bool await_ready() const noexcept { return false; } void await_resume() const noexcept { } void await_suspend(std::coroutine_handle<> coroutine) const noexcept { g_queue.push_back([coroutine] { coroutine.resume(); }); } }; template <typename T> struct task { struct promise_type { std::coroutine_handle<> precursor; T data; task get_return_object() noexcept { return {std::coroutine_handle<promise_type>::from_promise(*this)}; } std::suspend_never initial_suspend() noexcept { std::cout << std::coroutine_handle<promise_type>::from_promise(*this).address() << " initial_suspend" << std::endl; return {}; } void unhandled_exception() { } std::suspend_never final_suspend() noexcept { std::cout << std::coroutine_handle<promise_type>::from_promise(*this).address() << " final_suspend" << std::endl; if (precursor) { g_queue.push_back([this] { precursor.resume(); }); } return {}; } void return_value(T value) noexcept { data = std::move(value); } }; bool await_ready() const noexcept { std::cout << handle.address() << " await_ready" << std::endl; return false; } T await_resume() const noexcept { std::cout << handle.address() << " await_resume" << std::endl; return std::move(handle.promise().data); } void await_suspend(std::coroutine_handle<> coroutine) const noexcept { std::cout << handle.address() << " await_suspend (precursor = " << coroutine.address() << ")" << std::endl; if (handle.done()) coroutine.resume(); handle.promise().precursor = coroutine; } std::coroutine_handle<promise_type> handle; }; task<int> bar() { // co_await next_tick(); co_return 42; } task<int> foo() { co_return co_await bar(); } int main() { auto coroutine = foo(); while (!g_queue.empty()) { auto fn = g_queue.front(); g_queue.pop_front(); fn(); } std::cout << "Result: " << coroutine.handle.promise().data << std::endl; }
内容的提问来源于stack exchange,提问作者Maël Nison
相关产品推荐
相关产品推荐

