挂起的协程句柄是否应始终在程序结束前销毁?

Should the suspended coroutine handle be always destroyed before program ends?

看看这个简化的例子:

std::coroutine_handle<> logger;
const char* next_msg = nullptr;
void log(const char* msg)
{
    next_msg = msg;
    if (logger) logger.resume();
}

struct wait_msg {
    bool await_ready() {
        return next_msg != nullptr;
    }
    void await_suspend(std::coroutine_handle<> h) {
        logger = h;
    }
    auto await_resume() {
        const char* msg = next_msg;
        next_msg = nullptr;
        return msg;
    }
};

struct procedure {
    struct promise_type;
    using handle_type = std::coroutine_handle<promise_type>;
    
    handle_type handle;

    struct promise_type {
        procedure get_return_object() { 
            return { handle_type::from_promise(*this)};
        }
        std::suspend_never initial_suspend() { return {}; }
        std::suspend_never final_suspend() noexcept { return {}; }
        void return_void() {}
        void unhandled_exception() { std::terminate(); }
    };
};

procedure log_messages()
{
    int c = 1;
    for (;;) {
        const char* msg = co_await wait_msg{};
        std::cout << c++ << ": " << msg << std::endl;
    }
}

假设我在初始点或最终点没有暂停,协程在 co_await 暂停,是否应该在程序结束前销毁协程句柄?

或者:可以从 main 中删除第 p.handle.destroy(); 行吗?

int main() {
    log("Hello World!");

    auto p = log_messages();
    
    log("Hello World, again!");
    log("Hello World, and again!");
    
    // is the next line needed?
    p.handle.destroy();
}

查看完整内容 demo

有问题的代码的先前版本 here

Should the suspended coroutine handle be always destroyed before program ends?

没有,有些情况下destroy()是不需要的,甚至是不可能的。

,在问题的代码中,协程句柄上需要destroy()

standard 中的这句话解释了事情:

The coroutine state is destroyed when control flows off the end of the coroutine or the destroy member function ([coroutine.handle.resumption]) of a coroutine handle ([coroutine.handle]) that refers to the coroutine is invoked.

因为这个协程永远不会跳出无限循环 (for(;;)) - 它必须通过它的句柄销毁。

procedure log_messages()
{
    int c = 1;
    for (;;) {
        const char* msg = co_await wait_msg{};
        std::cout << c++ << ": " << msg << std::endl;
    }
}

但是,可以稍微修改代码 - 例如 - returns nullptr 当没有更多行要打印时,并在 nullptr 上中断循环:

procedure log_messages()
{
    int c = 1;
    for (;;) {
        const char* msg = co_await wait_msg{};
        if (!msg) break; // !!!
        std::cout << c++ << ": " << msg << std::endl;
    }
}


在这种情况下 - 不需要甚至不可能使用 destroy(),因为它会导致 UB。 这是因为 destroy() 只能在挂起的协程上调用,而我们在问题代码的最后一点没有挂起:

    struct promise_type {
        //...
        std::suspend_never final_suspend() noexcept { return {}; }
        //...
    };
};