“terminate called without an active exception” after pthread_cancel
在探究这个问题的条件时,出现了一个问题,例如下面的代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | #include <iostream> #include <thread> #include <chrono> #include <stdexcept> #include <cxxabi.h> using namespace std; // mocking external library call stuck in a strictly user-land infinite loop int buggy_function_simulation() { // cout <<"In buggy function" << endl; // (1) int counter = 0; while (true) { if ( ++counter == 1000000 ) { counter = 0; } } return 0; } int main(int argc, char **argv) { cout <<"Hello, world!" << endl; auto lambda = []() { pthread_setcanceltype( PTHREAD_CANCEL_ASYNCHRONOUS, nullptr ); // cout <<"ID:"<<pthread_self() <<endl; // (2) try { cout <<"ID:"<<pthread_self() <<endl; // (3) buggy_function_simulation(); } catch ( abi::__forced_unwind& ) { cout <<"thread cancelled!" << endl; // (4) throw; } }; std::thread th(lambda); pthread_t id = th.native_handle(); cout << id << endl; this_thread::sleep_for(chrono::seconds(1)); cout <<"cancelling ID:"<< id << endl; pthread_cancel(id); th.join(); cout <<"cancelled:"<< id << endl; return 0; } |
编译并运行导致中止:
1 2 3 4 5 6 7 8 9 | $ g++ -g -Og -std=c++11 -pthread -o test test.cpp -lpthread $ ./test Hello, world! 139841296869120 ID: 139841296869120 cancelling ID: 139841296869120 terminate called without an active exception Aborted (core dumped) $ |
请注意,不会出现诊断输出(4)。
如果我注释掉(3)并取消注释(2),结果是:
1 2 3 4 5 6 7 | $ ./test Hello, world! 139933357348608 ID: 139933357348608 cancelling ID: 139933357348608 cancelled: 139933357348608 $ |
同样,(4)的输出未出现(为什么?),但已取消中止操作。
或者,如果我保留(3),将(2)注释掉,然后取消注释(1),则结果最终符合预期:
1 2 3 4 5 6 7 8 9 | $ ./test Hello, world! 139998901511936 ID: 139998901511936 In buggy function cancelling ID: 139998901511936 thread cancelled! cancelled: 139998901511936 $ |
因此,问题是:
- 在第一种情况下,"在没有活动异常的情况下终止调用"的原因是什么?
- 为什么在第二种情况下未激活挡块?
- 为什么在第三种情况下不加注释(1)会有如此不同?
出于完整性考虑,以下是第一种情况下gdb的堆栈跟踪:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | Program terminated with signal SIGABRT, Aborted. #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51 51 ../sysdeps/unix/sysv/linux/raise.c: No such file or directory. [Current thread is 1 (Thread 0x7f5d9b49a700 (LWP 12130))] (gdb) where #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:51 #1 0x00007f5d9b879801 in __GI_abort () at abort.c:79 #2 0x00007f5d9bece957 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #3 0x00007f5d9bed4ab6 in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #4 0x00007f5d9bed4af1 in std::terminate() () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #5 0x00007f5d9bed44ba in __gxx_personality_v0 () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #6 0x00007f5d9bc3a708 in ?? () from /lib/x86_64-linux-gnu/libgcc_s.so.1 #7 0x00007f5d9bc3acfc in _Unwind_ForcedUnwind () from /lib/x86_64-linux-gnu/libgcc_s.so.1 #8 0x00007f5d9c1dbf10 in __GI___pthread_unwind (buf=<optimized out>) at unwind.c:121 #9 0x00007f5d9c1d0d42 in __do_cancel () at ./pthreadP.h:297 #10 sigcancel_handler (sig=<optimized out>, si=0x7f5d9b499bb0, ctx=<optimized out>) at nptl-init.c:215 #11 <signal handler called> #12 buggy_function_simulation () at test.cpp:15 #13 0x0000558865838227 in <lambda()>::operator() (__closure=<optimized out>) at test.cpp:29 #14 std::__invoke_impl<void, main(int, char**)::<lambda()> > (__f=...) at /usr/include/c++/7/bits/invoke.h:60 #15 std::__invoke<main(int, char**)::<lambda()> > (__fn=...) at /usr/include/c++/7/bits/invoke.h:95 #16 std::thread::_Invoker<std::tuple<main(int, char**)::<lambda()> > >::_M_invoke<0> (this=<optimized out>) at /usr/include/c++/7/thread:234 #17 std::thread::_Invoker<std::tuple<main(int, char**)::<lambda()> > >::operator() (this=<optimized out>) at /usr/include/c++/7/thread:243 #18 std::thread::_State_impl<std::thread::_Invoker<std::tuple<main(int, char**)::<lambda()> > > >::_M_run(void) ( this=<optimized out>) at /usr/include/c++/7/thread:186 #19 0x00007f5d9beff66f in ?? () from /usr/lib/x86_64-linux-gnu/libstdc++.so.6 #20 0x00007f5d9c1d26db in start_thread (arg=0x7f5d9b49a700) at pthread_create.c:463 #21 0x00007f5d9b95a88f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95 |
如果从标记为
请勿在C中使用
更新:
如果将异步取消与旨在支持该代码的代码一起使用,则可以这样做。通过谨慎使用
对于同步取消,如果声明为
的析构函数。
因此,在使用异步取消时,对任意C库代码的任何调用(因此可能会使用析构函数构造C对象)都具有潜在的危险,并且必须调用