Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tcp读取数据的时候,断开后一段时间后出现死锁 #302

Open
gooker opened this issue Jul 31, 2023 · 17 comments
Open

tcp读取数据的时候,断开后一段时间后出现死锁 #302

gooker opened this issue Jul 31, 2023 · 17 comments

Comments

@gooker
Copy link

gooker commented Jul 31, 2023

centos7.8 gcc 10

场景很常见,目前一个设备,大约每秒1次执行下协程读取网口状态, 开始网络正常,然后断开设备网络,几个小时甚至更多就出现死锁,如下下数据 @yyzybb537 这个锁是libgo内部的吧

 go co_scheduler(sched_test) [=](){
                dev_tcp_status_check(id);
            };

死锁情况

Thread 10 (Thread 0x7f9a267c0700 (LWP 47760)):
#0  0x00007f9a2e0c854d in __lll_lock_wait () from /lib64/libpthread.so.0
#1  0x00007f9a2e0c3e9b in _L_lock_883 () from /lib64/libpthread.so.0
#2  0x00007f9a2e0c3d68 in pthread_mutex_lock () from /lib64/libpthread.so.0
#3  0x0000000000bec9aa in __gthread_mutex_lock (__mutex=<optimized out>) at /opt/rh/devtoolset-10/root/usr/include/c++/10/x86_64-redhat-linux/bits/gthr-default.h:749
#4  std::mutex::lock (this=<optimized out>) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/std_mutex.h:100
#5  std::unique_lock<std::mutex>::lock (this=0x7f9a267bc900) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unique_lock.h:138
#6  std::unique_lock<std::mutex>::unique_lock (__m=..., this=0x7f9a267bc900) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/unique_lock.h:68
#7  libgo::RoutineSyncTimerT<std::mutex, std::condition_variable>::join_unschedule (id=..., this=0x7f9a2000a050) at /mnt/hgfs/vmshare/github/libgo/libgo/scheduler/../task/../routine_sync/timer.h:128
#8  co::Processer::WakeupBySelf(co::IncursivePtr<co::Task> const&, unsigned long, std::function<void ()> const&) (this=0x208c360, tkPtr=..., id=<optimized out>, functor=...) at /mnt/hgfs/vmshare/github/libgo/libgo/scheduler/processer.cpp:384
#9  0x0000000000bed19f in co::Processer::Wakeup(co::Processer::SuspendEntry const&, std::function<void ()> const&) (entry=..., functor=...) at /mnt/hgfs/vmshare/github/libgo/libgo/scheduler/processer.cpp:366
#10 0x0000000000c081f1 in co::ReactorElement::TriggerListWithoutLock (this=<optimized out>, revent=28, entryList=std::vector of length 1, capacity 1 = {...}) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/std_function.h:337
#11 0x0000000000c088c8 in co::ReactorElement::Trigger (this=this@entry=0x7f9a08001510, reactor=reactor@entry=0x7f9a2000a5c0, pollEvent=<optimized out>) at /mnt/hgfs/vmshare/github/libgo/libgo/netio/unix/reactor_element.cpp:78
#12 0x0000000000c0dc3a in co::EpollReactor::Run (this=0x7f9a2000a5c0) at /mnt/hgfs/vmshare/github/libgo/libgo/netio/unix/epoll_reactor.cpp:97
#13 0x0000000000c07711 in operator() (__closure=0x7f9a2000a5e8) at /mnt/hgfs/vmshare/github/libgo/libgo/netio/unix/reactor.cpp:47
#14 std::__invoke_impl<void, co::Reactor::InitLoopThread()::<lambda()> > (__f=...) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/invoke.h:60
#15 std::__invoke<co::Reactor::InitLoopThread()::<lambda()> > (__fn=...) at /opt/rh/devtoolset-10/root/usr/include/c++/10/bits/invoke.h:95
#16 std::thread::_Invoker<std::tuple<co::Reactor::InitLoopThread()::<lambda()> > >::_M_invoke<0> (this=0x7f9a2000a5e8) at /opt/rh/devtoolset-10/root/usr/include/c++/10/thread:264
#17 std::thread::_Invoker<std::tuple<co::Reactor::InitLoopThread()::<lambda()> > >::operator() (this=0x7f9a2000a5e8) at /opt/rh/devtoolset-10/root/usr/include/c++/10/thread:271
#18 std::thread::_State_impl<std::thread::_Invoker<std::tuple<co::Reactor::InitLoopThread()::<lambda()> > > >::_M_run(void) (this=0x7f9a2000a5e0) at /opt/rh/devtoolset-10/root/usr/include/c++/10/thread:215
#19 0x00007f9a2faba4e0 in execute_native_thread_routine () from /lib/libpaho-mqttpp3.so.1
#20 0x00007f9a2e0c1ea5 in start_thread () from /lib64/libpthread.so.0
#21 0x00007f9a2d5cb8dd in clone () from /lib64/libc.so.6
@paradiseforgithub
Copy link

paradiseforgithub commented Jul 31, 2023 via email

@gooker
Copy link
Author

gooker commented Aug 1, 2023

连接的时候使用如下的非阻塞方式,不确定对libgo 有没有影响

//非阻塞方式连接
        int error=-1, len;
        len = sizeof(int);
        struct timeval tm;
        fd_set set;
        unsigned long ul = 1;
        ioctl(sockfd, FIONBIO, &ul); //设置为非阻塞模式
        if( connect(sockfd,(struct sockaddr *)&plcaddr,sizeof(plcaddr)) == -1)
        {
            tm={out_time,0};//10s
            //tm.tv_set = TIME_OUT_TIME;
            //tm.tv_uset = 0;
            FD_ZERO(&set);
            FD_SET(sockfd, &set);
            if(select(sockfd+1, NULL, &set, NULL, &tm) > 0)
            {
                getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
                if(error == 0)
                    ret = true;
                else
                    ret = false;
            }
            else
                ret = false;
        }
        else
            ret = true;
        
        ul = 0;
        ioctl(sockfd, FIONBIO, &ul); //设置为阻塞模式

@qinrhx
Copy link

qinrhx commented Aug 21, 2023

这库还维护不?我也遇到了死锁,服务跑几天就会出现,__lll_lock_wait只有一个线程出来,然后好几个线程在LFLock上面,把cpu都跑满了,提交的协程也不再执行,截几个图出来看有没帮助
image
image
image
image
image

@gooker
Copy link
Author

gooker commented Aug 21, 2023

@qinrhx 这个协程调用函数有锁,协程外也对这个锁也有处理,容易锁. 我这里只是用了tcp非阻塞遇到问题,改成阻塞貌似还正常.

@qinrhx
Copy link

qinrhx commented Aug 25, 2023

@qinrhx 这个协程调用函数有锁,协程外也对这个锁也有处理,容易锁. 我这里只是用了tcp非阻塞遇到问题,改成阻塞貌似还正常.

我看更新记录,近期有更换了协程锁套件。应该是这个锁套件有bug,我换回原来的版本,跑了一个星期没出现死锁。 @gooker

@gooker
Copy link
Author

gooker commented Aug 26, 2023

@qinrhx 多谢,等有时间我试试,不好复现

@SignalEmit
Copy link

我也遇到了这个问题,难受的一逼莫名奇妙死锁了。你用的是哪个版本

@SignalEmit
Copy link

@qinrhx 这个协程调用函数有锁,协程外也对这个锁也有处理,容易锁. 我这里只是用了tcp非阻塞遇到问题,改成阻塞貌似还正常.

我看更新记录,近期有更换了协程锁套件。应该是这个锁套件有bug,我换回原来的版本,跑了一个星期没出现死锁。 @gooker

你换的哪个版本啊

@SignalEmit
Copy link

@qinrhx 这个协程调用函数有锁,协程外也对这个锁也有处理,容易锁. 我这里只是用了tcp非阻塞遇到问题,改成阻塞貌似还正常.

我看更新记录,近期有更换了协程锁套件。应该是这个锁套件有bug,我换回原来的版本,跑了一个星期没出现死锁。 @gooker

@gooker 你用的哪个版本啊,跪求

@gooker
Copy link
Author

gooker commented Sep 15, 2023

@qinrhx 这个协程调用函数有锁,协程外也对这个锁也有处理,容易锁. 我这里只是用了tcp非阻塞遇到问题,改成阻塞貌似还正常.

我看更新记录,近期有更换了协程锁套件。应该是这个锁套件有bug,我换回原来的版本,跑了一个星期没出现死锁。 @gooker

@gooker 你用的哪个版本啊,跪求

我用的最新的,暂时没测试没换版本

@SignalEmit
Copy link

@qinrhx 这个协程调用函数有锁,协程外也对这个锁也有处理,容易锁. 我这里只是用了tcp非阻塞遇到问题,改成阻塞貌似还正常.

我看更新记录,近期有更换了协程锁套件。应该是这个锁套件有bug,我换回原来的版本,跑了一个星期没出现死锁。 @gooker

@gooker 你用的哪个版本啊,跪求

我用的最新的,暂时没测试没换版本

@gooker matser分支不是还存在死锁问题嘛,我换3.1-stable发现这个版本有bug,携程有时候会空转

@qinrhx
Copy link

qinrhx commented Sep 18, 2023

@qinrhx 这个协程调用函数有锁,协程外也对这个锁也有处理,容易锁. 我这里只是用了tcp非阻塞遇到问题,改成阻塞貌似还正常.

我看更新记录,近期有更换了协程锁套件。应该是这个锁套件有bug,我换回原来的版本,跑了一个星期没出现死锁。 @gooker

你换的哪个版本啊

@SignalEmit 具体哪个版本不记得了,是之前从master下载的,你找master提交记录更换锁套件之前的试试吧,比如这个
image

@SignalEmit
Copy link

@qinrhx 这个协程调用函数有锁,协程外也对这个锁也有处理,容易锁. 我这里只是用了tcp非阻塞遇到问题,改成阻塞貌似还正常.

我看更新记录,近期有更换了协程锁套件。应该是这个锁套件有bug,我换回原来的版本,跑了一个星期没出现死锁。 @gooker

你换的哪个版本啊

@SignalEmit 具体哪个版本不记得了,是之前从master下载的,你找master提交记录更换锁套件之前的试试吧,比如这个 image
@qinrhx 我切过这个版本,这个版本有bug。举个例子
go{
while(true){
sleep(1);
}
};
这个休眠挂起这里会出bug,无限空转,sleep也失效了。还会影响其他携程。3.1的稳定版也有这问题

@qinrhx
Copy link

qinrhx commented Sep 18, 2023

@qinrhx 这个协程调用函数有锁,协程外也对这个锁也有处理,容易锁. 我这里只是用了tcp非阻塞遇到问题,改成阻塞貌似还正常.

我看更新记录,近期有更换了协程锁套件。应该是这个锁套件有bug,我换回原来的版本,跑了一个星期没出现死锁。 @gooker

你换的哪个版本啊

@SignalEmit 具体哪个版本不记得了,是之前从master下载的,你找master提交记录更换锁套件之前的试试吧,比如这个 image
@qinrhx 我切过这个版本,这个版本有bug。举个例子
go{
while(true){
sleep(1);
}
};
这个休眠挂起这里会出bug,无限空转,sleep也失效了。还会影响其他携程。3.1的稳定版也有这问题

哦,这我倒没测过,我没有在协程里做循环休眠的。
之前是听说定时器有内存泄漏,失效的bug,你看看能不能改下业务逻辑避免掉了。
这个库看上去bug不少,也不怎么维护了,所以能不用就别用了。。。要用也简单用

@SignalEmit
Copy link

@gooker @qinrhx

我这两天仔细看了一下这个代码,发现这个锁加的不太好,巨大的区域锁(有空了代码仔细看明白了,我重构重构这个锁)。上面那个tcp模块的死锁我找到原因了
复现方法:写了个httpserver epoll方式。开50个client并发,一直发发get请求,不出30秒就死锁了。

测试出了两处死锁

死锁1:

线程1
routine_sync/timer.h run函数 timer.h 150行 bool locked = invoke_lock.try_lock();
scheduler/processer.cpp WakeupBySelf函数 processer:cpp 381行 std::unique_lockTaskQueue::lock_t lock(waitQueue_.LockRef());

线程2
scheduler/processer.cpp WakeupBySelf函数 processer:cpp 381行 std::unique_lockTaskQueue::lock_t lock(waitQueue_.LockRef());
routine_sync/timer.h run函数 timer.h 128行 std::unique_lock invoke_lock(*invoke_mtx);

我仔细看了一下代码waitQueue_.LockRef()这个锁不好改,这个锁不仅锁携程调度队列,还要task,改了会导致压入的数据校验的时候,多线程会置空。我得做法是屏蔽掉 routine_sync/timer.h run函数 timer.h 128行 std::unique_lock invoke_lock(*invoke_mtx); 写了个http并发示例测试了半个小时暂时没发现有异常。代码没细细研究,暂时不清楚拆了这块会导致什么异常问题。暂时看没发现有崩溃情况。

死锁2:
打开debug模式 DebugPrint宏里有个锁和TSQueue里的队列锁会形成死锁。

我得处理办法是 TSQueue size计数换成原子计数

1 similar comment
@SignalEmit
Copy link

@gooker @qinrhx

我这两天仔细看了一下这个代码,发现这个锁加的不太好,巨大的区域锁(有空了代码仔细看明白了,我重构重构这个锁)。上面那个tcp模块的死锁我找到原因了
复现方法:写了个httpserver epoll方式。开50个client并发,一直发发get请求,不出30秒就死锁了。

测试出了两处死锁

死锁1:

线程1
routine_sync/timer.h run函数 timer.h 150行 bool locked = invoke_lock.try_lock();
scheduler/processer.cpp WakeupBySelf函数 processer:cpp 381行 std::unique_lockTaskQueue::lock_t lock(waitQueue_.LockRef());

线程2
scheduler/processer.cpp WakeupBySelf函数 processer:cpp 381行 std::unique_lockTaskQueue::lock_t lock(waitQueue_.LockRef());
routine_sync/timer.h run函数 timer.h 128行 std::unique_lock invoke_lock(*invoke_mtx);

我仔细看了一下代码waitQueue_.LockRef()这个锁不好改,这个锁不仅锁携程调度队列,还要task,改了会导致压入的数据校验的时候,多线程会置空。我得做法是屏蔽掉 routine_sync/timer.h run函数 timer.h 128行 std::unique_lock invoke_lock(*invoke_mtx); 写了个http并发示例测试了半个小时暂时没发现有异常。代码没细细研究,暂时不清楚拆了这块会导致什么异常问题。暂时看没发现有崩溃情况。

死锁2:
打开debug模式 DebugPrint宏里有个锁和TSQueue里的队列锁会形成死锁。

我得处理办法是 TSQueue size计数换成原子计数

@nqf
Copy link

nqf commented Nov 15, 2023

现在这个库不怎么维护了是大问题,都不敢用了

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants