对协同路由句柄调用destroy会导致segfault 我最近开始使用GCC-10进行了C++协同程序的实验。

对协同路由句柄调用destroy会导致segfault 我最近开始使用GCC-10进行了C++协同程序的实验。,c++,gcc,segmentation-fault,coroutine,c++-coroutine,C++,Gcc,Segmentation Fault,Coroutine,C++ Coroutine,下面的代码执行的方式与预期的完全相同,直到main exits,这会破坏导致\u coro.destroy()的任务实例语句到segfault 这是为什么 #包括 #包括 课堂任务 { 公众: 结构承诺型; 使用handle=std::coroutine\u handle; 结构承诺类型 { std::suspend_never initial_suspend()const noexcept{return{};} std::suspend_never final_suspend()const n

下面的代码执行的方式与预期的完全相同,直到main exits,这会破坏导致
\u coro.destroy()的
任务
实例语句到segfault

这是为什么

#包括
#包括
课堂任务
{
公众:
结构承诺型;
使用handle=std::coroutine\u handle;
结构承诺类型
{
std::suspend_never initial_suspend()const noexcept{return{};}
std::suspend_never final_suspend()const noexcept{return{};}
自动获取返回对象()noexcept{return task{handle::from_promise(*this)};}
无效未处理的_异常(){std::terminate();}
};
~task(){if(_coro!=nullptr){std::cout有几件事:

建议使用
suspend\u always
或返回
coroutine\u句柄
final\u suspend
恢复,然后手动
.destroy()
析构函数中的句柄

~我的任务(){
if(handle)handle.destroy();
}
这是因为返回
suspend\u never
可能会导致句柄被销毁,就像Raymond Chen一样,这意味着您需要手动跟踪句柄可能处于活动状态或可能被销毁的位置


在我的例子中,segfault是由于双重空闲造成的,如果句柄被复制到其他任何地方,就会发生这种情况。请记住,
coroutine\u handle
是指向一个coroutine状态的非所有句柄,因此多个副本可以指向相同(可能已释放)的内存

假设您没有执行任何类型的手动析构函数调用,请确保在任何给定的时间内也只使用句柄的一个副本

请记住,在许多情况下,复制语义将取代移动语义,因此需要删除复制构造函数和赋值运算符

您还必须定义移动构造函数,因为(根据CPPFREFERENCE,请谨慎考虑)
coroutine\u句柄
上的移动构造函数是隐式声明的构造函数,这意味着内部指针仅被复制,而没有设置为
nullptr
(中提供了更多信息)

因此,默认移动构造函数将产生两个完全“有效”(因此
bool(handle)=true
coroutine\u handle
对象,导致多个coroutine析构函数尝试
.destroy()
单个coroutine实例,导致双重空闲,从而导致潜在的segfault

为我的任务分类{
协程(手柄),;
公众:
内联my_任务(my_任务&&o):句柄(o.handle){
o、 handle=nullptr;//重要!
}
~my_task(){
if(handle)handle.destroy();
}
我的任务(const my_task&)=删除;
my_task&operator=(const my_task&)=删除;
};


请注意,Valgrind是调试此类错误的首选工具。在撰写本文时,Apt存储库中提供的Valgrind有点过时,并且不支持io操作系统调用,因此它将因错误而阻塞。并且构建/安装以获得io操作的修补程序。

,因为您的承诺是
最终暂停
suspend_never
,您说的是“允许协同程序在完成后自毁”。然后您在
~任务
析构函数中手动销毁,这是一个双重免费错误。这看起来是解决此问题的方法。谢谢。