简体   繁体   中英

C++ different threads have the same thread ID on FreeBSD 10

I am using a C++ logging library on a FreeBSD 10 machine and I am running into trouble closing threads when receiving a sigint .

A created a GitHub project for testing purposes ( link ). If you build it on FreeBSD 10, execute it and press [ctrl+c] it will terminate. You can find the build commands I use below.

$ git clone git@github.com: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

Code I used (can also be found in the GitHub repository)

/* 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;
}

Correct output (on eg MacOS Sierra):

As you can see the ID's of the threads are different, as expected.

$ ./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

Wrong output (termination, on FreeBSD 10.3):

The thread ID's are the same here, which is pretty weird.

$ ./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 dump

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 (obj=obj@entry=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*&&) (
    __f=@0x801c5e050: (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*&&) (__fn=@0x801c5e050: (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

System information

$ 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

The original issue I created can be found on GitHub ( link ), however there is no fix yet.

I hope someone will be able to help me fix this issue. Thanks in advance.

That's not a bug, that's a feature.

You don't get a guarantee about where your signal is going to be delivered, and the set of things you're allowed to do in a signal handler is restricted.

See sigaction(3) for details about what you can do (and you can't do anything else). Your program is doing many things that are not allowed in a signal handler.

The correct thing to do is to signal something else in your program and return from the signal handler. An example technique for doing that is the "self pipe trick". Create a pipe and keep a handle to both ends. Read from one end in your normal I/O processing. If you get a signal, in the signal handler, write a byte to the other end of the pipe and return. When the byte is read from the pipe you know the signal has arrived and you can do extended processing safely.

Update:

As Michael Burr has pointed out, you can block particular threads from receiving particular signals using pthread_sigmask(3) . However, to fix the underlying problem here you still need to not do the work in the signal handler.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM