Dav*_*ger 3 c++ continuations c++20 c++-coroutine
背景
\n\n在确信C++ 无堆栈协程非常棒之后。我一直在为我的代码库实现协程,并意识到 Final_Suspend 中的一个奇怪之处。
\n\n语境
\n\n让\xe2\x80\x99s 假设你有以下final_suspend函数:
\n\nfinal_awaitable final_suspend() noexcept\n{\n return {};\n}\nRun Code Online (Sandbox Code Playgroud)\n\n并且,final_awaitable的实现如下:
\n\nstruct final_awaitable\n{\n bool await_ready() const noexcept\n {\n return false;\n }\n default_handle_t await_suspend( promise_handle_t h ) const noexcept\n { \n return h.promise().continuation();\n }\n void await_resume() const noexcept {}\n};\nRun Code Online (Sandbox Code Playgroud)\n\n如果这里的延续是从任务队列中原子检索的,并且任务队列可能为空(这可能发生在await_ready和await_suspend之间的任何时间),那么await_suspend必须能够返回一个空白延续。
\n\n据我了解,当await_suspend返回句柄时,返回的句柄立即恢复(N4775草案中的5.1)。因此,如果此处没有可用的延续,则任何应用程序都会崩溃,因为在从wait_suspend接收到无效的协程句柄后,会在无效的协程句柄上调用恢复。
\n\n以下是执行顺序:
\n\nfinal_suspend Constructs final_awaitable.\n final_awaitable::await_ready Returns false, triggering await_suspend.\n final_awaitable::await_suspend Returns a continuation (or empty continuation).\n continuation::resume This could be null if a retrieved from an empty work queue.\nRun Code Online (Sandbox Code Playgroud)\n\n似乎没有为有效句柄指定检查(就像await_suspend 返回 bool 一样)。
\n\n问题
\n\n这里是一个导致崩溃的人为示例。
\n\n解决方案的想法
\n\n使用一个虚拟任务,该任务是co_yield的无限循环。这是一种浪费的周期,我宁愿不必这样做,而且我还需要为每个执行线程创建虚拟任务的单独句柄,这看起来很愚蠢。
创建std::coroutine_handle的特化,其中resume不执行任何操作,返回该句柄的实例。我不想专门研究标准库。这也不起作用,因为coroutine_handle<>没有did()和resume()作为虚拟的。
编辑 1 16/03/2020调用 continuation() 以原子方式检索延续并将结果存储在Final_awaitable结构中,如果没有可用的延续,await_ready world 将返回 true。如果有可用的延续, await_ready将返回 false,则将调用wait_suspend并返回延续(立即恢复它)。\n这不起作用,因为任务返回的值存储在协程框架中,并且如果该值仍然需要,那么协程框架一定不能被破坏。在这种情况下,它会在final_awaitable上调用await_resume后被销毁。\n只有当该任务是连续链中的最后一个任务时,这才会出现问题。
编辑2 - 20/03/2020忽略从await_suspend返回可用的co 例程句柄的可能性。仅从顶级 co 例程恢复继续。这看起来效率不高。
2020年1月4日
\n\n我仍然没有找到没有实质性缺点的解决方案。我想我之所以陷入这个问题是因为await_suspend似乎是为了解决这个确切的问题而设计的(能够返回coroutine_handle)。我只是无法弄清楚预期的模式。
\n