views:

589

answers:

2

I have trouble getting boost:thread to work. It runs fine when compiling without optimization:

g++ -o test-thread test-thread.cpp -lboost_thread-gcc-mt-s -lpthread
./test-thread

But a version that's compiled with optimizations crashes

g++ -O2 -o test-thread test-thread.cpp -lboost_thread-gcc-mt-s -lpthread
./test-thread
Segmentation fault

Does anyone know what might be the reason?

Here is the code I am using:

#include <boost/thread.hpp>
#include <boost/function.hpp>

void task1() {
  // do something
}
void task2() {
  // do something
}

int main (int argc, char ** argv) {
  using namespace boost;

  function0<void> f1(&task1);
  function0<void> f2(&task2);

  thread thread_1(f1);
  thread thread_2(f2);

  // do other stuff 
  thread_2.join();
  thread_1.join();
  return 0;
}

P.S.: I am using boost 1.32 on ubuntu linux.

UPDATE:

Here is where it crashes in the debugger (where line 37 is the one with thread_2.join(); in my original code):

(gdb) bt
#0  0x080499e0 in boost::thread::join ()
#1  0x080496b8 in main (argc=1, argv=0xbfea3eb4) at ../src/test-thread.cpp:37

Here are my actual two functions:

void task1() {
  std::cerr << "THREAD 1 START" << std::endl;
  for(double i=0; i<999999; ++i){
    std::cout << i << std::endl;
  }
  std::cerr << "THREAD 1 END" << std::endl;
}

void task2() {
  std::cerr << "THREAD 2 START" << std::endl;
  for(double i=0; i<999999; ++i){
    std::cout << i << std::endl;
  }
  std::cerr << "THREAD 2 END" << std::endl;
}

Thanks for any help!

+1  A: 

Hi,

Could you make sure that core dumps are active (ulimit -c unlimited), compile with symbols (-O2 -g), run, and open the stack trace in gdb? (gdb test-thread core, then type backtrace at the gdb prompt, quit to quit)

UPDATE

Based on the backtrace, the segmentation fault occurs in boost::thread::join. Could you re-open the core and:

  • obtain a disassembly of the boost::thread::join method:
    • disassemble boost::thread::join
    • disassemble
  • obtain a dump of the registers:
    • info registers

The implementation of boost::thread::join (boost 1.32 official source) is pretty straight-forward, so unless the Ubuntu binaries grossly diverge from the official code (which we'll hopefully find out from the disassembly), there are only two possible causes for the segmentation fault.

Can you also confirm how far in their printouts the two threads got before the segmentation fault?

void thread::join()
{
    int res = 0;
#if defined(BOOST_HAS_WINTHREADS)
    res = WaitForSingleObject(reinterpret_cast<HANDLE>(m_thread), INFINITE);
    assert(res == WAIT_OBJECT_0);
    res = CloseHandle(reinterpret_cast<HANDLE>(m_thread));
    assert(res);
#elif defined(BOOST_HAS_PTHREADS)
    res = pthread_join(m_thread, 0);
    assert(res == 0);
#elif defined(BOOST_HAS_MPTASKS)
    OSStatus lStatus = threads::mac::detail::safe_wait_on_queue(
        m_pJoinQueueID, NULL, NULL, NULL, kDurationForever);
    assert(lStatus == noErr);
#endif
    // This isn't a race condition since any race that could occur would
    // have us in undefined behavior territory any way.
    m_joinable = false;
}

Usual causes for these kinds of problems:

  • uninitialized variables
  • variables that are accessed by both threads that were not marked volatile
  • inlined calls and/or unrolled loops (whose stack frame size is the sum of all inlined method instances' stack frames) being called recursively and overflowing the stack much faster than the non-inlined versions

Cheers, V.

vladr
Thanks so much for your help again. I found the solution now. I posted it as a separate answer.
+1  A: 

I found the bug! I had linked against the wrong version of the library. I had been using boost_thread-gcc-mt-s, but it works with boost_thread-gcc-mt instead:

g++ -O2 -o test-thread test-thread.cpp -lboost_thread-gcc-mt -lpthread

I looked into the boost documentation, but I couldn't find any information about what the differences between those library versions are. There is also a boost_thread-gcc-mt-d, which I assume is the debug version, but linking against that one always results in binaries that segfault, even when compiling with -g. But at least I can run the threads now.