Possible data race while reading 2 files - c++

I am trying to read the contents of the same file using 2 different threads in C++. Helgrind complains of a possible data race. Since reading the contents of the file does not change the state of the file, I do not understand why Helgrind is reporting a problem.
Below is Helgrind's message:
Possible data race during write of size 8 at 0x6647828 by thread #3
==10434== Locks held: none
==10434== at 0x4C2C387: free (vg_replace_malloc.c:537)
==10434== by 0x58E61B6: fclose##GLIBC_2.2.5 (iofclose.c:84)
==10434== by 0x50D454D: std::\__basic_file\<char\>::close() (basic_file.cc:277)
==10434== by 0x510F6CD: std::basic_filebuf\<char, std::char_traits\<char\> \>::close() (fstream.tcc:166)
==10434== by 0x510F9B4: \~basic_filebuf (fstream:220)
==10434== by 0x510F9B4: std::basic_ifstream\<char, std::char_traits\<char\> \>::\~basic_ifstream() (fstream:499)
==10434== by 0x401286: fn(void\*) (in a.out)
==10434== by 0x4C30EEE: mythread_wrapper (hg_intercepts.c:387)
==10434== by 0x4E43EA4: start_thread (pthread_create.c:307)
==10434== by 0x597696C: clone (clone.S:111)
==10434==
==10434== This conflicts with a previous write of size 8 by thread #2
==10434== Locks held: none
==10434== at 0x58F437F: \_IO_un_link (genops.c:81)
==10434== by 0x58E61DC: fclose##GLIBC_2.2.5 (iofclose.c:54)
==10434== by 0x50D454D: std::\__basic_file\<char\>::close() (basic_file.cc:277)
==10434== by 0x510F6CD: std::basic_filebuf\<char, std::char_traits\<char\> \>::close() (fstream.tcc:166)
==10434== by 0x510F9B4: \~basic_filebuf (fstream:220)
==10434== by 0x510F9B4: std::basic_ifstream\<char, std::char_traits\<char\> \>::\~basic_ifstream() (fstream:499)
==10434== by 0x401286: fn(void\*) (in a.out)
==10434== by 0x4C30EEE: mythread_wrapper (hg_intercepts.c:387)
==10434== by 0x4E43EA4: start_thread (pthread_create.c:307)
Below is the code to reproduce the above issue:
#include <fstream>
#include <iostream>
#include <stdio.h>
using namespace std;
void* fn(void* parameter) {
ifstream inputFile("sample", ios::in);
while (!inputFile.eof()) {
string line;
getline(inputFile, line);
}
return NULL;
}
int main() {
pthread_t t1, t2;
pthread_create(&t1, NULL, fn, NULL);
pthread_create(&t2, NULL, fn, NULL);
pthread_join(t1, NULL);
pthread_join(t2, NULL);
}
I tried to find out if reading a file changes the state of the file but I could not find any possible explanation. I am expecting that there is no data race when reading a file paralelly.

Related

glibc pthread_join crash when call pthread_join() twice

I have written the following code using the POSIX pthread library:
#include <stdlib.h>
#include <pthread.h>
void *thread_function(void *arg) {
char *code = "1";
}
int main() {
int res;
pthread_t a_thread;
void *thread_result;
res = pthread_create(&a_thread, NULL, thread_function, NULL);
if (res != 0) {
perror("Thread creation failed");
exit(EXIT_FAILURE);
}
sleep(5);
printf("\nWaiting for thread to finish...\n");
res = pthread_join(a_thread, &thread_result);
printf("res[%d]\n",res);
if (res != 0) {
perror("Thread join failed");
exit(EXIT_FAILURE);
}
res = pthread_join(a_thread, &thread_result);
printf("res[%d]\n",res);
exit(EXIT_SUCCESS);
}
On executing the code I got the following output:
Waiting for thread to finish...
res[0]
Segmentation fault (core dumped)
In the code, I want to test What happens if you call the pthread_jion() function
twice after the thread is finished. The first call to the function is correct, and the second crash. The backtrace:
Core was generated by `./a.out'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 pthread_join (threadid=140150565050112, thread_return=0x7fffa0a2c508) at
pthread_join.c:47
47 if (INVALID_NOT_TERMINATED_TD_P (pd))
(gdb) bt
Python Exception exceptions.ImportError No module named gdb.frames:
#0 pthread_join (threadid=140150565050112, thread_return=0x7fffa0a2c508) at
pthread_join.c:47
#1 0x00000000004008d5 in main ()
And I check the pthread_join.c file:
39 int
40 pthread_join (threadid, thread_return)
41 pthread_t threadid;
42 void **thread_return;
43 {
44 struct pthread *pd = (struct pthread *) threadid;
45
46 /* Make sure the descriptor is valid. */
47 if (INVALID_NOT_TERMINATED_TD_P (pd))
48 /* Not a valid thread handle. */
49 return ESRCH;
In the line 47, the Macro definition checks Whether the pd is a valid thread handle. If not, return ESRCH(3).
However when I run the same code in another Linux environment, I got the following output:
Waiting for thread to finish...
res[0]
res[3]
Does it have anything to do with the environment? The two linux systems have same ldd version:
ldd (GNU libc) 2.17
same GLIBC:
GLIBCXX_3.4
GLIBCXX_3.4.1
GLIBCXX_3.4.2
GLIBCXX_3.4.3
GLIBCXX_3.4.4
GLIBCXX_3.4.5
GLIBCXX_3.4.6
GLIBCXX_3.4.7
GLIBCXX_3.4.8
GLIBCXX_3.4.9
GLIBCXX_3.4.10
GLIBCXX_3.4.11
GLIBCXX_3.4.12
GLIBCXX_3.4.13
GLIBCXX_3.4.14
GLIBCXX_3.4.15
GLIBCXX_3.4.16
GLIBCXX_3.4.17
GLIBCXX_3.4.18
GLIBCXX_3.4.19
GLIBCXX_3.4.20
GLIBC_2.3
GLIBC_2.2.5
GLIBC_2.14
GLIBC_2.17
GLIBC_2.3.2
GLIBCXX_FORCE_NEW
GLIBCXX_DEBUG_MESSAGE_LENGT
and the same linux kernel version:
Red Hat Enterprise Linux Server release 6.6 (Santiago)
pthread_join calls pthread_detach after the thread has terminated.
pthread_detach releases all the threads resources when the thread terminates.
From the documentation of pthread_detach
Attempting to detach an already detached thread results in
unspecified behavior.
So you have unspecified behaviour, so you can't guarantee what will happen afterwards.
At the very least, the memory pointed to by the threadid will be freed, leading to accessing freed memory.
In short, don't call pthread_join twice on the same threadid. Why would you want to?
Edit: even simpler: the man page for pthread_join says:
Joining with a thread that has previously been joined results in
undefined behavior.

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 e.g. 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.

thread sanitizer is not showing data race

I tried the program given here
small_race.c
#include <pthread.h>
int Global;
void *Thread1(void *x) {
Global = 42;
return x;
}
int main() {
pthread_t t;
pthread_create(&t, NULL, Thread1, NULL);
Global = 43;
pthread_join(t, NULL);
return Global;
}
compilation
$ clang -fsanitize=thread -g -pthread -O1 small_race.c
$./a.out ==> No error it's passing successfully
I tried to create 2 more thread and also try to sleep in one of thread then also it's passing. I am using Debian OS
Something is wrong with your platform or installation. With your exact code, I get:
==================
WARNING: ThreadSanitizer: data race (pid=20087)
Write of size 4 at 0x000000601080 by thread T1:
#0 Thread1(void*) /tmp/a.cpp:4 (a2+0x000000400a7f)
#1 <null> <null> (libtsan.so.0+0x0000000235b9)
Previous write of size 4 at 0x000000601080 by main thread:
#0 main /tmp/a.cpp:10 (a2+0x000000400ac5)
Location is global '<null>' of size 0 at 0x000000000000 (a2+0x000000601080)
Thread T1 (tid=20089, running) created by main thread at:
#0 pthread_create <null> (libtsan.so.0+0x000000027a67)
#1 main /tmp/a.cpp:9 (a2+0x000000400abb)
SUMMARY: ThreadSanitizer: data race /tmp/a.cpp:4 Thread1(void*)
==================

Interruption in a linux multithreaded app generate SIGSEGV

I have a x86_64 multithreaded application where I try to emulate avr interrupts: when interrupt occurs application and all threads are suspended while interrupt handler execute defined actions.
I thought signals were the solution to do this so I'm trying to catch SIGUSR1 signal, but when I raise SIGUSR1 program exit with a segmentation fault error before executing apply function. (I have tried mutex in isr and signals but removed as actually they run in the main thread)
The code is within 5 files.
isr.h
#ifndef __ISR_H__
#define __ISR_H__
typedef void (*routine_t)(void);
class InterruptServiceRoutine
{
routine_t routine;
bool locked = true;
public:
InterruptServiceRoutine(routine_t isr);
void apply();
void unlock(){locked = false;};
};
typedef InterruptServiceRoutine ISR_t;
#endif // __ISR_H__
isr.cpp:
#include <ISR.h>
InterruptServiceRoutine::InterruptServiceRoutine(routine_t isr): routine(isr){}
void InterruptServiceRoutine::apply()
{
if (!locked) routine();
}
Signals.h:
#ifndef __SIGNALS_H__
#define __SIGNALS_H__
#include <vector>
#include <ISR.h>
#include <signal.h>
class Logger;
class Signals
{
private:
Logger& log;
std::vector<ISR_t*> isr_table;
public:
Signals(Logger&);
~Signals();
unsigned int count(void);
void clear(void);
void connect(ISR_t &isr);
void apply(int);
};
#endif // __SIGNALS_H__
Signals.cpp:
#include <signal.h>
#include <pthread.h>
#include <functional>
#include <ISR.h>
#include <Signals.h>
#include <Logger.h>
using namespace std;
void Signals::apply(int)
{
sigset_t sigs_to_block;
sigfillset(&sigs_to_block);
pthread_sigmask(SIG_BLOCK, &sigs_to_block, NULL);
unsigned int num_interrupts = count();
for (unsigned int i=0; i < num_interrupts; ++i)
{
isr_table[i]->apply();
}
pthread_sigmask(SIG_SETMASK, NULL, NULL);
}
Signals::Signals(Logger& _log): log(_log)
{
clear();
auto _apply = bind1st(mem_fun(&Signals::apply), this);
struct sigaction new_action;
new_action.sa_handler = (__sighandler_t) &_apply;
new_action.sa_flags = 0;
sigfillset(&new_action.sa_mask);
sigaction(SIGUSR1, &new_action, NULL);
pthread_sigmask(SIG_SETMASK, NULL, NULL);
}
Signals::~Signals() {
struct sigaction new_action;
new_action.sa_handler = SIG_DFL;
new_action.sa_flags = 0;
sigemptyset(&new_action.sa_mask);
sigaddset(&new_action.sa_mask, SIGUSR1);
sigaction(SIGUSR1, &new_action, NULL);
clear();
}
void Signals::clear(void)
{
isr_table.clear();
isr_table.reserve(20);
}
unsigned int Signals::count(void)
{
return isr_table.size();
}
void Signals::connect(ISR_t &isr)
{
isr_table.push_back(&isr);
}
SignalsTest.test:
#include <signal.h>
#include <pthread.h>
#include <cxxtest/TestSuite.h>
#include <ISR.h>
#include <Signals.h>
#include <Logger.h>
volatile sig_atomic_t isr_called_count;
void isr(void)
{
++isr_called_count;
}
class SignalsTestSuite: public CxxTest::TestSuite
{
Logger log;
Signals handler;
public:
SignalsTestSuite(void): handler(log){}
void setUp()
{
handler.clear();
isr_called_count = 0;
}
/* ... truncated for more visibility ... */
void testWhenRaiseSIGUSRItCallsAvailableRoutine(void)
{
ISR_t routine(&isr);
routine.unlock();
handler.connect(routine);
handler.connect(routine);
raise(SIGUSR1);
TS_ASSERT_EQUALS(isr_called_count, 2);
}
};
Debug informations:
GNU gdb (Gentoo 7.7.1 p1) 7.7.1
Copyright (C) 2014 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law. Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://bugs.gentoo.org/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./out/signals...done.
(gdb) r
Starting program: test/out/signals
warning: Could not load shared library symbols for linux-vdso.so.1.
Do you need "set solib-search-path" or "set sysroot"?
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Running 3 tests..
Program received signal SIGUSR1, User defined signal 1.
0x00007ffff7bd0beb in raise (sig=10) at ../sysdeps/unix/sysv/linux/pt-raise.c:36
36 return INLINE_SYSCALL (tgkill, 3, pid, THREAD_GETMEM (THREAD_SELF, tid),
(gdb) bt
#0 0x00007ffff7bd0beb in raise (sig=10) at ../sysdeps/unix/sysv/linux/pt-raise.c:36
#1 0x0000000000403f38 in SignalsTestSuite::testWhenRaiseSIGUSRItCallsAvailableRoutine (this=this#entry=0x631cc0 <suite_SignalsTestSuite>)
at test/SignalsTest.test:62
#2 0x0000000000403f92 in TestDescription_suite_SignalsTestSuite_testWhenRaiseSIGUSRItCallsAvailableRoutine::runTest (this=<optimized out>) at out/signals.cpp:38
#3 0x000000000040424e in CxxTest::RealTestDescription::run (this=<optimized out>) at /opt/cxxtest/cxxtest/RealDescriptions.cpp:109
#4 0x0000000000409afb in CxxTest::TestRunner::runTest (this=this#entry=0x7fffffffd29f, td=...) at /opt/cxxtest/cxxtest/TestRunner.h:85
#5 0x0000000000409c76 in CxxTest::TestRunner::runSuite (this=this#entry=0x7fffffffd29f, sd=...) at /opt/cxxtest/cxxtest/TestRunner.h:72
#6 0x0000000000409e15 in CxxTest::TestRunner::runWorld (this=this#entry=0x7fffffffd29f) at /opt/cxxtest/cxxtest/TestRunner.h:57
#7 0x0000000000409f52 in CxxTest::TestRunner::runAllTests (listener=...) at /opt/cxxtest/cxxtest/TestRunner.h:34
#8 0x0000000000409f93 in CxxTest::ErrorFormatter::run (this=this#entry=0x7fffffffd320) at /opt/cxxtest/cxxtest/ErrorFormatter.h:59
#9 0x000000000040aa89 in CxxTest::Main<CxxTest::ErrorPrinter> (tmp=..., argc=argc#entry=1, argv=argv#entry=0x7fffffffd448) at /opt/cxxtest/cxxtest/TestMain.h:109
#10 0x0000000000407f62 in main (argc=1, argv=0x7fffffffd448) at out/runner.cpp:18
(gdb) l
31 that. */
32 pid_t pid = THREAD_GETMEM (THREAD_SELF, pid);
33 if (__glibc_unlikely (pid < 0))
34 pid = -pid;
35
36 return INLINE_SYSCALL (tgkill, 3, pid, THREAD_GETMEM (THREAD_SELF, tid),
37 sig);
38 }
(gdb) s
Program received signal SIGSEGV, Segmentation fault.
0x00007fffffffd210 in ?? ()
(gdb) bt
#0 0x00007fffffffd210 in ?? ()
#1 <signal handler called>
#2 0x00007ffff7bd0beb in raise (sig=10) at ../sysdeps/unix/sysv/linux/pt-raise.c:36
#3 0x0000000000403f38 in SignalsTestSuite::testWhenRaiseSIGUSRItCallsAvailableRoutine (this=this#entry=0x631cc0 <suite_SignalsTestSuite>)
at test/SignalsTest.test:62
#4 0x0000000000403f92 in TestDescription_suite_SignalsTestSuite_testWhenRaiseSIGUSRItCallsAvailableRoutine::runTest (this=<optimized out>) at out/signals.cpp:38
#5 0x000000000040424e in CxxTest::RealTestDescription::run (this=<optimized out>) at /opt/cxxtest/cxxtest/RealDescriptions.cpp:109
#6 0x0000000000409afb in CxxTest::TestRunner::runTest (this=this#entry=0x7fffffffd29f, td=...) at /opt/cxxtest/cxxtest/TestRunner.h:85
#7 0x0000000000409c76 in CxxTest::TestRunner::runSuite (this=this#entry=0x7fffffffd29f, sd=...) at /opt/cxxtest/cxxtest/TestRunner.h:72
#8 0x0000000000409e15 in CxxTest::TestRunner::runWorld (this=this#entry=0x7fffffffd29f) at /opt/cxxtest/cxxtest/TestRunner.h:57
#9 0x0000000000409f52 in CxxTest::TestRunner::runAllTests (listener=...) at /opt/cxxtest/cxxtest/TestRunner.h:34
#10 0x0000000000409f93 in CxxTest::ErrorFormatter::run (this=this#entry=0x7fffffffd320) at /opt/cxxtest/cxxtest/ErrorFormatter.h:59
#11 0x000000000040aa89 in CxxTest::Main<CxxTest::ErrorPrinter> (tmp=..., argc=argc#entry=1, argv=argv#entry=0x7fffffffd448) at /opt/cxxtest/cxxtest/TestMain.h:109
#12 0x0000000000407f62 in main (argc=1, argv=0x7fffffffd448) at out/runner.cpp:18
(gdb) l
Line number 39 out of range; ../sysdeps/unix/sysv/linux/pt-raise.c has 38 lines.
(gdb) s
Cannot find bounds of current function
(gdb) c
Continuing.
Program terminated with signal SIGSEGV, Segmentation fault.
The program no longer exists.
Memory informations:
$ valgrind --leak-check=full --show-leak-kinds=all ./out/signals
==31715== Memcheck, a memory error detector
==31715== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==31715== Using Valgrind-3.10.1 and LibVEX; rerun with -h for copyright info
==31715== Command: ./out/signals
==31715==
Running 3 tests..==31715==
==31715== Process terminating with default action of signal 11 (SIGSEGV)
==31715== Bad permissions for mapped region at address 0xFFEFFF1F0
==31715== at 0xFFEFFF1F0: ???
==31715== by 0x409F92FF: ???
==31715== by 0xFFF: ???
==31715== by 0x40AA88FF: ???
==31715==
==31715== HEAP SUMMARY:
==31715== in use at exit: 176 bytes in 2 blocks
==31715== total heap usage: 2 allocs, 0 frees, 176 bytes allocated
==31715==
==31715== 16 bytes in 1 blocks are still reachable in loss record 1 of 2
==31715== at 0x4C294C0: operator new(unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==31715== by 0x408AB1: CxxTest::ErrorPrinter::ErrorPrinter(std::ostream&, char const*, char const*) (ErrorPrinter.h:43)
==31715== by 0x407F4C: main (runner.cpp:17)
==31715==
==31715== 160 bytes in 1 blocks are still reachable in loss record 2 of 2
==31715== at 0x4C294C0: operator new(unsigned long) (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==31715== by 0x40B2E3: __gnu_cxx::new_allocator<InterruptServiceRoutine*>::allocate(unsigned long, void const*) (new_allocator.h:104)
==31715== by 0x40B315: std::_Vector_base<InterruptServiceRoutine*, std::allocator<InterruptServiceRoutine*> >::_M_allocate(unsigned long) (in test/out/signals)
==31715== by 0x40B941: InterruptServiceRoutine** std::vector<InterruptServiceRoutine*, std::allocator<InterruptServiceRoutine*> >::_M_allocate_and_copy<std::move_iterator<InterruptServiceRoutine**> >(unsigned long, std::move_iterator<InterruptServiceRoutine**>, std::move_iterator<InterruptServiceRoutine**>) (stl_vector.h:1138)
==31715== by 0x40BA28: std::vector<InterruptServiceRoutine*, std::allocator<InterruptServiceRoutine*> >::reserve(unsigned long) (vector.tcc:75)
==31715== by 0x40AE7D: Signals::clear() (Signals.cpp:57)
==31715== by 0x40AF50: Signals::Signals(Logger&) (Signals.cpp:32)
==31715== by 0x403531: SignalsTestSuite::SignalsTestSuite() (in test/out/signals)
==31715== by 0x40302D: __static_initialization_and_destruction_0(int, int) (signals.cpp:18)
==31715== by 0x403144: _GLOBAL__sub_I_suite_SignalsTestSuite_init (signals.cpp:39)
==31715== by 0x4147EC: __libc_csu_init (elf-init.c:88)
==31715== by 0x5A8DA34: (below main) (libc-start.c:245)
==31715==
==31715== LEAK SUMMARY:
==31715== definitely lost: 0 bytes in 0 blocks
==31715== indirectly lost: 0 bytes in 0 blocks
==31715== possibly lost: 0 bytes in 0 blocks
==31715== still reachable: 176 bytes in 2 blocks
==31715== suppressed: 0 bytes in 0 blocks
==31715==
==31715== For counts of detected and suppressed errors, rerun with: -v
==31715== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Erreur de segmentation
I've tried to figure out why there are missing symbols (0x00007fffffffd210 in ?? () and at 0xFFEFFF1F0: ??? ...):
(gdb) info share
From To Syms Read Shared Object Library
0x00007ffff7ddca80 0x00007ffff7df5960 Yes /lib64/ld-linux-x86-64.so.2
No linux-vdso.so.1
0x00007ffff7bc6a40 0x00007ffff7bd2781 Yes /lib64/libpthread.so.0
0x00007ffff79bb360 0x00007ffff79be0dc Yes /lib64/librt.so.1
0x00007ffff770f5f0 0x00007ffff77733c3 Yes /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/libstdc++.so.6
0x00007ffff73c04f0 0x00007ffff7425266 Yes /lib64/libm.so.6
0x00007ffff71a7ac0 0x00007ffff71b6e45 Yes /usr/lib/gcc/x86_64-pc-linux-gnu/4.8.4/libgcc_s.so.1
0x00007ffff6e2c430 0x00007ffff6f53b44 Yes /lib64/libc.so.6
But can't find a way to get "linux-vdso.so.1" on gentoo.
I'm a beginner in C++ what I'm doing wrong with memory ?
Have you a tip to fix this ?
Edit1:
routine_t and __sighandler_t types are aliases for: void (*)(void)
I believe that you can safely ignore the error. A StackOverflow member with a reputation in the top 0.25% of members (!) says that this warning "Could not load shared library symbols for linux-vdso.so.1." is something you can safely ignore. To see the post, go here:
Could not load shared library symbols for linux-vdso.so.1. while debugging
Problem solved thanks to Igor Tandetnik.
Finally the following code function.
signals.h:
#ifndef __SIGNALS_H__
#define __SIGNALS_H__
#include <vector>
#include <ISR.h>
#include <signal.h>
using namespace std;
class Logger;
class Signals
{
private:
Logger& log;
static bool action_handled;
void reserve();
public:
static void start();
static void stop();
static std::vector<ISR_t*> isr_table;
Signals(Logger& _log);
~Signals();
unsigned int count(void);
void clear(void);
void connect(ISR_t&);
static void apply(int);
static void unmask_pthread_signals();
static void mask_pthread_signals();
};
#endif // __SIGNALS_H__
signals.cpp:
#include <functional>
#include <signal.h>
#include <pthread.h>
#include <ISR.h>
#include <Signals.h>
#include <Logger.h>
using namespace std;
vector<ISR_t*> Signals::isr_table={};
bool Signals::action_handled=false;
void Signals::apply(int)
{
mask_pthread_signals();
for (unsigned int i=0; i < isr_table.size(); ++i)
{
isr_table[i]->apply();
}
unmask_pthread_signals();
}
Signals::Signals(Logger& _log): log(_log){
clear();
start();
}
Signals::~Signals() {
clear();
stop();
}
void Signals::clear(void)
{
isr_table.clear();
isr_table.reserve(0);
}
unsigned int Signals::count(void)
{
return isr_table.size();
}
void Signals::start()
{
if (action_handled) return;
struct sigaction new_action;
new_action.sa_handler = (__sighandler_t) &Signals::apply;
new_action.sa_flags = 0;
sigemptyset(&new_action.sa_mask);
sigaction(SIGUSR1, &new_action, NULL);
unmask_pthread_signals();
action_handled = true;
}
void Signals::stop()
{
if (!action_handled) return;
struct sigaction new_action;
new_action.sa_handler = SIG_DFL;
sigemptyset(&new_action.sa_mask);
sigaction(SIGUSR1, &new_action, NULL);
mask_pthread_signals();
action_handled = false;
}
void Signals::unmask_pthread_signals()
{
sigset_t signals_mask;
sigemptyset(&signals_mask);
pthread_sigmask(SIG_SETMASK, &signals_mask, NULL);
}
void Signals::mask_pthread_signals()
{
sigset_t signals_mask;
sigfillset(&signals_mask);
pthread_sigmask(SIG_SETMASK, &signals_mask, NULL);
}
void Signals::reserve()
{
if (isr_table.capacity() <= isr_table.size()) {
isr_table.reserve(isr_table.capacity() + 20);
}
}
void Signals::connect(ISR_t &isr)
{
start();
reserve();
isr_table.push_back(&isr);
}

program deadlock involving __unregister_atfork & TCMalloc

Consider the following C++ program. I expect that the first thread to invoke exit will terminate the program. This is what happens when I compile it with g++ -g test.cxx -lpthread. However, when I link against TCMalloc (g++ -g test.cxx -lpthread -ltcmalloc), it hangs. Why?
Examination of the stack frames suggests that the first thread to invoke exit is stuck in __unregister_atfork waiting on some sort of reference-counted variable to reach 0. Since it previously acquired the mutex, all other threads become deadlocked. My guess is that there is some sort of interaction betweek tcmalloc's atfork handlers and my code.
Tested on CentOS 6.4 with gperftools 2.0.
$ cat test.cxx
#include <unistd.h>
#include <iostream>
#include <pthread.h>
#include <stdlib.h>
using namespace std;
static pthread_mutex_t m = PTHREAD_MUTEX_INITIALIZER;
static void* task(void*) {
if (fork() == 0)
return NULL;
pthread_mutex_lock(&m);
exit(0);
}
int main(int argc, char **argv) {
cout << getpid() << endl;
pthread_t t;
for (unsigned i = 0; i < 100; ++i) {
pthread_create(&t, NULL, task, NULL);
}
sleep(9999);
}
$ g++ -g test.cxx -lpthread && $ ./a.out
19515
$ g++ -g test.cxx -lpthread -ltcmalloc && ./a.out
24252
<<< process hangs indefinitely >>>
^C
$ pstack 24252
Thread 101 (Thread 0x7ffaabdf7700 (LWP 24253)):
#0 0x000000328c4f84c4 in __unregister_atfork () from /lib64/libc.so.6
#1 0x00007ffaac02d2c6 in __do_global_dtors_aux () from /usr/lib64/libtcmalloc.so.4
#2 0x0000000000000000 in ?? ()
Thread 100 (Thread 0x7ffaab3f6700 (LWP 24254)):
#0 0x000000328cc0e054 in __lll_lock_wait () from /lib64/libpthread.so.0
#1 0x000000328cc09388 in _L_lock_854 () from /lib64/libpthread.so.0
#2 0x000000328cc09257 in pthread_mutex_lock () from /lib64/libpthread.so.0
#3 0x0000000000400abf in task(void*) ()
#4 0x000000328cc07851 in start_thread () from /lib64/libpthread.so.0
#5 0x000000328c4e894d in clone () from /lib64/libc.so.6
<<< the other 98 threads are also deadlocked >>>
Thread 1 (Thread 0x7ffaabdf9740 (LWP 24252)):
#0 0x000000328c4acbcd in nanosleep () from /lib64/libc.so.6
#1 0x000000328c4aca40 in sleep () from /lib64/libc.so.6
#2 0x0000000000400b33 in main ()
EDIT: I think the problem might be that exit is not thread-safe. According to POSIX, exit is thread-safe. However, the glibc documentation states that exit is not thread-safe.