C++20 协程使用 Final_suspend 进行延续

Dav*_*ger 3 c++ continuations c++20 c++-coroutine

背景

\n\n

在确信C++ 无堆栈协程非常棒之后。我一直在为我的代码库实现协程,并意识到 Final_Suspend 中的一个奇怪之处。

\n\n

语境

\n\n

让\xe2\x80\x99s 假设你有以下final_suspend函数:

\n\n
final_awaitable final_suspend() noexcept\n{\n    return {};\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

并且,final_awaitable的实现如下:

\n\n
struct 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};\n
Run Code Online (Sandbox Code Playgroud)\n\n

如果这里的延续是从任务队列中原子检索的,并且任务队列可能为空(这可能发生在await_readyawait_suspend之间的任何时间),那么await_suspend必须能够返回一个空白延续。

\n\n

据我了解,当await_suspend返回句柄时,返回的句柄立即恢复(N4775草案中的5.1)。因此,如果此处没有可用的延续,则任何应用程序都会崩溃,因为在从wait_suspend接收到无效的协程句柄后,会在无效的协程句柄上调用恢复。

\n\n

以下是执行顺序:

\n\n
final_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.\n
Run Code Online (Sandbox Code Playgroud)\n\n

似乎没有为有效句柄指定检查(就像await_suspend 返回 bool 一样)。

\n\n

问题

\n\n
    \n
  1. 在这种情况下,您如何在没有锁的情况下将工作队列添加到await_suspend ?寻找可扩展的解决方案。
  2. \n
  3. 为什么底层协程实现不检查有效句​​柄。
  4. \n
\n\n

这里是一个导致崩溃的人为示例。

\n\n

解决方案的想法

\n\n
    \n
  1. 使用一个虚拟任务,该任务是co_yield的无限循环。这是一种浪费的周期,我宁愿不必这样做,而且我还需要为每个执行线程创建虚拟任务的单独句柄,这看起来很愚蠢。

  2. \n
  3. 创建std::coroutine_handle的特化,其中resume不执行任何操作,返回该句柄的实例。我不想专门研究标准库。这也不起作用,因为coroutine_handle<>没有did()resume()作为虚拟的。

  4. \n
  5. 编辑 1 16/03/2020调用 continuation() 以原子方式检索延续并将结果存储在Final_awaitable结构中,如果没有可用的延续,await_ready world 将返回 true。如果有可用的延续, await_ready将返回 false,则将调用wait_suspend并返回延续(立即恢复它)。\n这不起作用,因为任务返回的值存储在协程框架中,并且如果该值仍然需要,那么协程框架一定不能被破坏。在这种情况下,它会在final_awaitable上调用await_resume后被销毁。\n只有当该任务是连续链中的最后一个任务时,这才会出现问题。

  6. \n
  7. 编辑2 - 20/03/2020忽略从await_suspend返回可用的co 例程句柄的可能性。仅从顶级 co 例程恢复继续。这看起来效率不高。

  8. \n
\n\n

2020年1月4日

\n\n

我仍然没有找到没有实质性缺点的解决方案。我想我之所以陷入这个问题是因为await_suspend似乎是为了解决这个确切的问题而设计的(能够返回coroutine_handle)。我只是无法弄清楚预期的模式。

\n