背景
[确信C++ stackless coroutines are pretty awesome。我一直在为代码库实现协程,并在final_suspend中发现了一个奇怪的地方。
上下文
假设您具有以下final_suspend函数:
final_awaitable final_suspend() noexcept
{
return {};
}
而且,final_awaitable的实现如下:
struct final_awaitable
{
bool await_ready() const noexcept
{
return false;
}
default_handle_t await_suspend( promise_handle_t h ) const noexcept
{
return h.promise().continuation();
}
void await_resume() const noexcept {}
};
如果从任务队列和中自动检索到此处的延续,则任务队列可能为空(可能在await_ready和await_suspend之间的任何时间发生),那么await_suspend必须能够返回一个空白的延续。
据我了解,当await_suspend返回一个句柄时,返回的句柄将立即恢复(N4775草案中的5.1)。因此,如果此处没有可用的继续,则任何应用程序都将崩溃,因为从await_suspend接收到恢复后,将在无效的协程句柄上调用简历。
以下是执行顺序:
final_suspend Constructs final_awaitable.
final_awaitable::await_ready Returns false, triggering await_suspend.
final_awaitable::await_suspend Returns a continuation (or empty continuation).
continuation::resume This could be null if a retrieved from an empty work queue.
似乎没有为有效的句柄指定检查(就像await_suspend返回bool一样。
问题
导致崩溃的人为的示例是here。
解决方案
使用虚拟任务,它是co_yield的无限循环。这是一种浪费的周期,我宁愿不必这样做,也需要为每个执行线程为虚拟任务创建单独的句柄,这似乎很愚蠢。
创建std::coroutine_handle的特殊化,其中简历不执行任何操作,返回该句柄的实例。我宁愿不专门研究标准库。这也不起作用,因为coroutine_handle <>没有虚拟的done()和resume()。
关于:(实际上只是一个很大的评论。)
struct final_awaitable
{
bool await_ready() const noexcept
{
return false;
}
bool await_suspend( promise_handle_t h ) const noexcept
{
auto continuation = h.promise().atomically_pop_a_continuation();
if (continuation)
continuation.handle().resume();
return true;//or whatever is meaningfull for your case.
}
void await_resume() const noexcept {}
};