1.客户端主动断开TCP连接,引发的段错误
2.定时任务执行后,引发的段错误
2.1.问题描述
在服务器启动非活跃连接超时销毁后,
当定时任务执行时,
触发了段错误;
下面是相关日志
bash
//相关日志:
//设定的定时任务为Connection::releaseInLoop()
[DEBUG][2025-12-19 15:07:08]TimerTask::~TimerTask(): execute timer task,id:1
[DEBUG][2025-12-19 15:07:08]TimerTask::~TimerTask(): execute timer task,id:2
[DEBUG][2025-12-19 15:07:08][tid=140399404377664]执行Connection::releaseInLoop(),conn_id=1
[DEBUG][2025-12-19 15:07:08][tid=140399412770368]执行Connection::releaseInLoop(),conn_id=2
Segmentation fault (core dumped)
2.2.问题定位
下面是TimerTask::~TimerTask()的具体实现
cpp
TimerTask::~TimerTask()
{
//如果定时任务没有被取消
if(false == _isCanceled)
{
lg(DEBUG,"TimerTask::~TimerTask(): execute timer task,id:%lu",_id);
_taskCb();//执行定时任务
}
//lg(DEBUG,"TimerTask::~TimerTask(): _deleteCb() ,id:%lu",_id);
//将id->TimerTask的映射关系从TimerWheel::_idMapTimerTask内移除
_deleteCb();
}
下面是Connection::releaseInLoop()的具体实现
cpp
void Connection::releaseInLoop()
{
#ifdef DEBUG
std::thread::id gtid=std::this_thread::get_id();
std::ostringstream oss;
oss << gtid;
std::string tid=oss.str();
lg(DEBUG,"[tid=%s]执行Connection::releaseInLoop(),conn_id=%lld",tid.c_str(),getId());
#endif
//assert(CONNECTED == _status ||DISCONNECTING == _status );
//修改连接状态
_status = DISCONNECTED;
//取消该连接的定时任务
_eventLoop->cancelTimerTask(_id);
//从epoll中移除该channel
_channel.remove();
//关闭描述符
_socket.close();
lg(DEBUG,"[tid=%s]_connId=%lld,Connection::releaseInLoop() 1",tid.c_str(),getId());
//调用用户设置的回调
if(_closeCb) _closeCb(shared_from_this());
// std::thread::id gtid=std::this_thread::get_id();
// std::ostringstream oss;
// oss << gtid;
// std::string tid=oss.str();
lg(DEBUG,"[tid=%s]_connId=%lld,Connection::releaseInLoop() 2",tid.c_str(),getId());
//调用组件本身设置的回调
if(_serverCloseCb) _serverCloseCb(shared_from_this());
lg(DEBUG,"[tid=%s]_connId=%lld,Connection::releaseInLoop() 3",tid.c_str(),getId());
}
Connection::releaseInLoop()没有执行后续的DEBUG日志代码,
说明问题出现在下面这段区间,
cpp
//assert(CONNECTED == _status ||DISCONNECTING == _status );
//修改连接状态
_status = DISCONNECTED;
//取消该连接的定时任务
_eventLoop->cancelTimerTask(_id);
//从epoll中移除该channel
_channel.remove();
//关闭描述符
_socket.close();
后续在cancelTimerTask(_id)定位到如下内容
cpp
void TimerWheel::cancelTaskToLoop(uint64_t id)
{
//lg(DEBUG,"TimerWheel::cancelTaskToLoop(uint64_t id) called");
if(exist(id) == false) return ; //判断id是否在TimerWheel::_idMapTimerTask内
//lg(DEBUG,"TimerWheel::cancelTaskToLoop(uint64_t id) 1");
std::shared_ptr<TimerTask> ptr=_idMapTimerTask[id].lock();
//lg(DEBUG,"TimerWheel::cancelTaskToLoop(uint64_t id) 2 ,ptr==nullptr:%d",(ptr==nullptr));
ptr->cancel();
//lg(DEBUG,"TimerWheel::cancelTaskToLoop(uint64_t id) over");
}
上述代码的**ptr->cancel();**是引发段错误的原因
2.3.问题产生原因分析
在时间轮TimerWheel模块的实现中,
有如下成员;
cpp
/// @brief 时间轮数组
std::vector<std::list<std::shared_ptr<TimerTask>>> _wheel;
/// @brief id->timeTask映射表
std::unordered_map<uint64_t,std::weak_ptr<TimerTask>> _idMapTimerTask;
当_wheel内的定时任务TimerTask结构体 调用析构函数执行定时任务(即Connection::releaseInLoop())时,
在此之前对应的shared_ptr的引用计数已经变为0 ,置为nullptr,且被释放;
因为**exist(id)**的返回结果为true,
因此会执行std::shared_ptr<TimerTask> ptr=_idMapTimerTask[id].lock()(这句代码位于Connection::releaseInLoop()中的_eventLoop->cancelTimerTask(_id);的执行流程内);
获取到的ptr的值为nullptr,
从而导致段错误
2.4.问题解决方案
修改TimerTask::~TimerTask(),在定时任务执行前移除id->TimerTask的映射关系,如下;
cpp
TimerTask::~TimerTask()
{
//将id->TimerTask的映射关系从TimerWheel::_idMapTimerTask内移除
_deleteCb();
//如果定时任务没有被取消
if(false == _isCanceled)
{
lg(DEBUG,"TimerTask::~TimerTask(): execute timer task,id:%lu",_id);
_taskCb();//执行定时任务
}
//lg(DEBUG,"TimerTask::~TimerTask(): _deleteCb() ,id:%lu",_id);
}