C++协程:从最后一个挂起点调用`handle.delety`是否有效?
从C++协程的最终挂起中调用handle.destroy()
是否有效?
根据我的理解,这应该很好,因为协程当前已暂停,不会再次恢复。
仍然,AddressSaniizer报告以下代码片段的heap-use-after-free
:
#include <experimental/coroutine>
#include <iostream>
using namespace std;
struct final_awaitable {
bool await_ready() noexcept { return false; }
void await_resume() noexcept {}
template<typename PROMISE> std::experimental::coroutine_handle<> await_suspend(std::experimental::coroutine_handle<PROMISE> coro) noexcept {
coro.destroy(); // Is this valid?
return std::experimental::noop_coroutine();
}
};
struct task {
struct promise_type;
using coro_handle = std::experimental::coroutine_handle<promise_type>;
struct promise_type {
task get_return_object() { return {}; }
auto initial_suspend() { return std::experimental::suspend_never(); }
auto final_suspend() noexcept { return final_awaitable(); }
void unhandled_exception() { std::terminate(); }
void return_void() {}
};
};
task foo() {
cerr << "foo
";
co_return;
}
int main() {
auto x = foo();
}
当使用clang 11.0.1和编译标志-stdlib=libc++ --std=c++17 -fcoroutines-ts -fno-exceptions -fsanitize=address
编译时。(参见https://godbolt.org/z/eq6eoc)
(我实际代码的简化版本。您可以在https://godbolt.org/z/8Yadv1)
中找到完整的代码这是我的代码中的问题还是AddressSaniizer中的错误肯定?
解决方案
如果您100%确定以后没有人会使用协程承诺,则它完全有效。调用coroutine_handle::destroy
等同于调用协程承诺析构函数。
final_suspend
返回std::suspend_never
std::suspend_never final_suspend() const noexcept { return {}; }
它等同于您的代码。如果我们想在协程结束后对协程承诺做一些有意义的事情,比如返回协程的存储结果,那么我们希望在final_suspend
中暂停协程。由于您的task
对象不存储或返回任何内容,我看不出为什么要最终挂起它。
coroutine_handle
在其他地方引用。回到第1点。对于我的库,它不安全,因为可能是result
对象仍在引用它。
总之,可以调用coroutine_promise::destroy
if:
- 协程暂停(当您达到
final_suspend
时) - 销毁后没有人会使用该协程承诺(请特别确保不存在引用该协程、类似于未来的对象!)
destroy
之前未调用(双删除)
相关文章