2017-05-30 71 views
1

我在FreeBSD 10机器上使用C++日志记录库,并且在收到sigint时遇到了关闭线程的麻烦。C++不同的线程在FreeBSD 10上有相同的线程ID

A为测试目的创建了一个GitHub项目(link)。如果你在FreeBSD 10上构建它,执行它并按[ctrl + c]它会终止。你可以在下面找到我使用的构建命令。我用

$ git clone [email protected]:tijme/free-bsd-thread-bug.git 
$ cd free-bsd-thread-bug && mkdir -p cmake-build-debug && cd cmake-build-debug 
$ cmake .. -DCMAKE_BUILD_TYPE=Debug -DCMAKE_C_COMPILER="/usr/local/bin/gcc6" -DCMAKE_CXX_COMPILER="/usr/local/bin/g++6" 
$ make -dA 
$ ./FreeBSDThreadBug 

代码(也可以在GitHub的仓库中找到)

/* main.cpp */ 

#include "Example.h" 
#include <iostream> 
#include <csignal> 
#include <thread> 
#include <chrono> 

Example* example = new Example(); 

void onSignal(int signum) 
{ 
    delete example; 
    exit(0); 
} 

int main() { 
    signal(SIGINT, onSignal); 

    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); 

    return 0; 
} 

/* Example.h */ 

#ifndef FREEBSDTHREADBUG_EXAMPLE_H 
#define FREEBSDTHREADBUG_EXAMPLE_H 

#include <thread> 
#include <iostream> 
#include <chrono> 

class Example { 

public: 
    Example(); 
    ~Example(); 
    std::thread threadHandle; 
    void threadFunction(); 
}; 


#endif //FREEBSDTHREADBUG_EXAMPLE_H 

/* Example.cpp */ 

#include "Example.h" 
#include <thread> 
#include <chrono> 

Example::Example() 
{ 
    std::cout << "Main: starting thread" << std::endl; 
    threadHandle = std::thread(&Example::threadFunction, this); 
    std::cout << "Main: thread started" << std::endl; 
} 

Example::~Example() 
{ 
    std::cout << "THIS ID: " << std::this_thread::get_id() << std::endl; 
    std::cout << "THREAD ID: " << threadHandle.get_id() << std::endl; 

    std::cout << "Main: joining thread" << std::endl; 
    threadHandle.join(); 
    std::cout << "Main: thread joined" << std::endl; 
} 

void Example::threadFunction() { 
    std::cout << "Thread: starting to sleep" << std::endl; 
    std::this_thread::sleep_for(std::chrono::milliseconds(2500)); 
    std::cout << "Thread: sleep finished" << std::endl; 
} 

正确的输出(例如MacOS的塞拉利昂):

正如你所看到的ID如预期的那样,线程是不同的。

$ ./FreeBSDThreadBug 
Main: starting thread 
Main: thread started 
Thread: starting to sleep 
^C 
THIS ID: 0x7fffa428a3c0 
THREAD ID: 0x70000c044000 
Main: joining thread 
Thread: sleep finished 
Main: thread joined 

错误输出(终止,在FreeBSD 10.3):

线程ID的是相同的位置,这是非常奇怪的。

$ ./FreeBSDThreadBug 
Main: starting thread 
Main: thread started 
Thread: starting to sleep 
^C 
THIS ID: 0x801c06800 
THREAD ID: 0x801c06800 
Main: joining thread 
terminate called after throwing an instance of 'std::system_error' 
    what(): Resource deadlock avoided 
Abort (core dumped) 

核心转储

Core was generated by `FreeBSDThreadBug'. 
Program terminated with signal SIGABRT, Aborted. 
#0 0x00000008012d335a in thr_kill() from /lib/libc.so.7 
[Current thread is 1 (LWP 100146)] 
(gdb) bt full 
#0 0x00000008012d335a in thr_kill() from /lib/libc.so.7 
No symbol table info available. 
#1 0x00000008012d3346 in raise() from /lib/libc.so.7 
No symbol table info available. 
#2 0x00000008012d32c9 in abort() from /lib/libc.so.7 
No symbol table info available. 
#3 0x0000000800ad8afd in __gnu_cxx::__verbose_terminate_handler() at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/libsupc++/vterminate.cc:95 
     terminating = true 
     t = <optimized out> 
#4 0x0000000800ad5b48 in __cxxabiv1::__terminate (handler=<optimized out>) at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:47 
No locals. 
#5 0x0000000800ad5bb1 in std::terminate() at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/libsupc++/eh_terminate.cc:57 
No locals. 
#6 0x0000000800ad5dc8 in __cxxabiv1::__cxa_throw ([email protected]=0x80200e0a0, tinfo=0x800dd0bc0 <typeinfo for std::system_error>, dest=0x800b073b0 <std::system_error::~system_error()>) 
    at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/libsupc++/eh_throw.cc:87 
     globals = <optimized out> 
#7 0x0000000800b04cd1 in std::__throw_system_error (__i=11) at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/src/c++11/functexcept.cc:130 
No locals. 
#8 0x0000000800b0792c in std::thread::join (this=0x801c5c058) at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/src/c++11/thread.cc:139 
     __e = <optimized out> 
#9 0x00000000004016fc in Example::~Example (this=0x801c5c058, __in_chrg=<optimized out>) at /root/FreeBSDThreadBug/Example.cpp:18 
No locals. 
#10 0x00000000004010b7 in onSignal (signum=2) at /root/FreeBSDThreadBug/main.cpp:11 
No locals. 
#11 0x000000080082fb4a in ??() from /lib/libthr.so.3 
No symbol table info available. 
#12 0x000000080082f22c in ??() from /lib/libthr.so.3 
No symbol table info available. 
#13 <signal handler called> 
No symbol table info available. 
#14 0x00000008012efb5a in _nanosleep() from /lib/libc.so.7 
No symbol table info available. 
#15 0x000000080082cc4c in ??() from /lib/libthr.so.3 
No symbol table info available. 
#16 0x000000000040155d in std::this_thread::sleep_for<long, std::ratio<1l, 1000l> > (__rtime=...) at /usr/local/lib/gcc6/include/c++/thread:322 
     __s = {__r = 2} 
     __ns = {__r = 500000000} 
     __ts = {tv_sec = 1, tv_nsec = 126917539} 
#17 0x000000000040177a in Example::threadFunction (this=0x801c5c058) at /root/FreeBSDThreadBug/Example.cpp:24 
No locals. 
#18 0x0000000000402432 in std::__invoke_impl<void, void (Example::* const&)(), Example*>(std::__invoke_memfun_deref, void (Example::* const&)(), Example*&&) (
    [email protected]: (void (Example::*)(Example * const)) 0x40172c <Example::threadFunction()>, __t=<unknown type in /root/FreeBSDThreadBug/cmake-build-debug/FreeBSDThreadBug, CU 0x552f, DIE 0xb2d7>) 
    at /usr/local/lib/gcc6/include/c++/functional:227 
No locals. 
#19 0x00000000004023bf in std::__invoke<void (Example::* const&)(), Example*>(void (Example::* const&)(), Example*&&) ([email protected]: (void (Example::*)(Example * const)) 0x40172c <Example::threadFunction()>, 
    __args#0=<unknown type in /root/FreeBSDThreadBug/cmake-build-debug/FreeBSDThreadBug, CU 0x552f, DIE 0xb2d7>) at /usr/local/lib/gcc6/include/c++/functional:251 
No locals. 
#20 0x0000000000402370 in std::_Mem_fn_base<void (Example::*)(), true>::operator()<Example*>(Example*&&) const (this=0x801c5e050, 
    __args#0=<unknown type in /root/FreeBSDThreadBug/cmake-build-debug/FreeBSDThreadBug, CU 0x552f, DIE 0xb2d7>) at /usr/local/lib/gcc6/include/c++/functional:604 
No locals. 
#21 0x000000000040233b in std::_Bind_simple<std::_Mem_fn<void (Example::*)()> (Example*)>::_M_invoke<0ul>(std::_Index_tuple<0ul>) (this=0x801c5e048) at /usr/local/lib/gcc6/include/c++/functional:1391 
No locals. 
#22 0x0000000000402289 in std::_Bind_simple<std::_Mem_fn<void (Example::*)()> (Example*)>::operator()() (this=0x801c5e048) at /usr/local/lib/gcc6/include/c++/functional:1380 
No locals. 
#23 0x0000000000402268 in std::thread::_State_impl<std::_Bind_simple<std::_Mem_fn<void (Example::*)()> (Example*)> >::_M_run() (this=0x801c5e040) at /usr/local/lib/gcc6/include/c++/thread:196 
No locals. 
#24 0x0000000800b0769f in std::execute_native_thread_routine (__p=0x801c5e040) at /wrkdirs/usr/ports/lang/gcc6/work/gcc-6.3.0/libstdc++-v3/src/c++11/thread.cc:83 
     __t = std::unique_ptr<std::thread::_State> containing 0x801c5e040 
#25 0x000000080082a855 in ??() from /lib/libthr.so.3 
No symbol table info available. 
#26 0x0000000000000000 in ??() 
No symbol table info available. 
Backtrace stopped: Cannot access memory at address 0x7fffdfffe000 

系统信息

$ freebsd-version 
10.3-RELEASE 

$ /usr/local/bin/gcc6 --version 
gcc6 (FreeBSD Ports Collection) 6.3.0 

$ /usr/local/bin/g++6 --version 
g++6 (FreeBSD Ports Collection) 6.3.0 

$ cmake --version 
cmake version 3.7.2 

我创建了最初的问题可以在GitHub(link)被发现,但没有修复呢。

我希望有人能够帮助我解决这个问题。提前致谢。

+5

对于在信号处理程序中你可以做什么,你是相当有限的,我猜想调用delete是许多你不应该在那里做的事情之一。 –

+0

你应该向FreeBSD的LibC维护者报告这个bug,而不是在这里发布它。 –

+2

同样来自http://en.cppreference.com/w/c/program/signal»如果在多线程程序中使用signal,则行为是不确定的。不需要线程安全。« –

回答

2

这不是一个错误,这是一个功能。

您无法保证您的信号将传送到何处,您在信号处理程序中允许执行的操作受到限制。

请参阅sigaction(3)了解有关您可以执行的操作(而且您无法执行其他任何操作)的详细信息。你的程序正在做许多信号处理程序中不允许的事情。

正确的做法是在程序中发出其他信号并从信号处理程序返回。这样做的一个示例技术是“自我管道技巧”。创建一个管道,并保持两端的句柄。从正常I/O处理的一端读取。如果你得到一个信号,在信号处理程序中,写一个字节到管道的另一端并返回。当从管道读取字节时,您知道信号已经到达,您可以安全地进行扩展处理。

更新:

正如迈克尔·伯尔指出,你可以使用pthread_sigmask(3)接收特定的信号阻止特定线程。然而,为了解决这个潜在的问题,你仍然不需要在信号处理器中完成这项工作。

+1

虽然它不会自动发生,但应该注意的是,在POSIX中,程序可以安排将信号传递到特定线程(或者可以安排信号到*不*交付给某些线程)。一个线程可以使用'pthread_sigmask()'函数来'取消'自己被调用来处理一个或多个信号的资格。 –

+0

@MichaelBurr是的,我同意'pthread_sigmask()'可以改变信号的传递位置。尽管OP仍然需要在信号处理器之外完成这项工作! (我知道你知道...) – janm

+0

谢谢,我会尽快查明。它在其他操作系统和更高版本的FreeBSD上工作是否真的很奇怪? – Tijme