Akira Okumura
Akira Okumura

Reputation: 1965

How can I avoid an exception thrown by <thread> in C++11?

I am trying to write multi-thread software using the thread library in C++11. Some basic tutorials found on the internet can compile and run as expected. But my own application separated into classes always throws an exception.

Could anyone kindly tell me which part of the code should be fixed, please?

$ clang++ -std=c++11 -stdlib=libc++ BaseInterface.cxx -c -o BaseInterface.o
$ clang++ -std=c++11 -stdlib=libc++ SocketReceiver.cxx -c -o SocketReceiver.o
$ clang++ -std=c++11 -stdlib=libc++ main.cxx -c -o main.o
$ clang++ -std=c++11 -stdlib=libc++ main.o BaseInterface.o SocketReceiver.o -o main
$ gdb main  
(gdb) run
Starting program: /Users/oxon/test/main 
Reading symbols for shared libraries ++............................ done
libc++abi.dylib: terminate called throwing an exception

Program received signal SIGABRT, Aborted.
[Switching to process 859 thread 0x40b]
0x00007fff88df8212 in __pthread_kill ()
(gdb) bt
#0  0x00007fff88df8212 in __pthread_kill ()
#1  0x00007fff8bc85af4 in pthread_kill ()
#2  0x00007fff8bcc9dce in abort ()
#3  0x00007fff894d3a17 in abort_message ()
#4  0x00007fff894d13c6 in default_terminate ()
#5  0x00007fff89874887 in _objc_terminate ()
#6  0x00007fff894d13f5 in safe_handler_caller ()
#7  0x00007fff894d1450 in std::terminate ()
#8  0x00007fff894d25b7 in __cxa_throw ()
#9  0x00007fff8a9ba3b9 in std::__1::thread::join ()
#10 0x0000000100000cf0 in SocketReceiver::Receive ()
#11 0x0000000100000c6d in SocketReceiver::DoReceive ()
#12 0x0000000100001593 in _ZNSt3__114__thread_proxyINS_5tupleIJPFPvS2_ES2_EEEEES2_S2_ ()
#13 0x00007fff8bc84742 in _pthread_start ()
#14 0x00007fff8bc71181 in thread_start ()

This is the result on OS X 10.8. Scientific Linux 6 with GCC 4.4 also gives a similar result.

= BaseInterface.h =

#ifndef BASE_INTERFACE_H
#define BASE_INTERFACE_H

#include "SocketReceiver.h"

class BaseInterface
{
private:
  SocketReceiver*    fReceiver;

public:
  BaseInterface();
  virtual ~BaseInterface();

  virtual void Close();
  virtual void Open();
;

#endif

= BaseInterface.cxx =

#include <iostream>

#include <string.h>
#include <unistd.h>

#include "BaseInterface.h"

BaseInterface::BaseInterface()
{
  fReceiver = new SocketReceiver(this);
}

//______________________________________________________________________________
BaseInterface::~BaseInterface()
{
  Close();
  delete fReceiver;
  fReceiver = 0;
}

//______________________________________________________________________________
void BaseInterface::Close()
{
  fReceiver->Stop();
  usleep(10000);

  while(fReceiver->IsRunning()){
    usleep(10000);
  } // while
}

//______________________________________________________________________________
void BaseInterface::Open()
{
  fReceiver->Start();

}

= SocketReceiver.h =

#ifndef SOCKET_RECEIVER_H
#define SOCKET_RECEIVER_H

#include <thread>
#include <mutex>

class BaseInterface;

class SocketReceiver
{
private:
  BaseInterface*             fInterface;
  bool                       fIsRunning;
  std::mutex                 fMutex;
  bool                       fStop;
  std::thread*               fThread;

public:
  SocketReceiver(BaseInterface* interface = 0);
  virtual ~SocketReceiver();

  bool             IsRunning() const {return fThread ? true : false;}
  static void*     DoReceive(void* arg);
  void             Receive();
  void             Start();
  void             Stop();
};

#endif

= SocketReceiver.cxx =

#include <iostream>
#include <thread>
#include <unistd.h>

#include "BaseInterface.h"
#include "SocketReceiver.h"

SocketReceiver::SocketReceiver(BaseInterface* interface)
{
  fInterface = interface;
  fStop = true;
  fThread = 0;
}

//______________________________________________________________________________
SocketReceiver::~SocketReceiver()
{
}

//______________________________________________________________________________
void* SocketReceiver::DoReceive(void* arg)
{
  SocketReceiver* receiver = (SocketReceiver*)arg;
  receiver->Receive();

  return 0;
}

//______________________________________________________________________________
void SocketReceiver::Receive()
{
  while(not fStop){
    fMutex.lock();
    usleep(10000);
    fMutex.unlock();
  } // while

  fThread->join();

  delete fThread;
  fThread = 0;
}

//______________________________________________________________________________
void SocketReceiver::Start()
{
  fStop = false;
  fThread  = new std::thread(DoReceive, (void*)this);
}

//______________________________________________________________________________
void SocketReceiver::Stop()
{
  fStop = true;
}

= main.cxx =

#include "BaseInterface.h"

int main()
{
  BaseInterface interface;
  interface.Open();
  interface.Close();

  return 0;
}

Upvotes: 1

Views: 2768

Answers (2)

Jonathan Wakely
Jonathan Wakely

Reputation: 171283

If the function run by a std::thread exits via and exception then std::terminate will be called.

std::thread::join() is specified to throw std::system_error with an error code resource_deadlock_would_occur if join() is called in the same thread, i.e. a thread cannot join itself. So your attempt to make the thread join itself causes an exception which terminates the process. A thread cannot join itself, joining is defined as "wait for the thread to complete" so obviously cannot be done by the thread itself, it would block waiting for itself to finish, which can't happen until the call to join() finishes, which can't happen until the thread finishes, which can't happen until the call to join() finishes ... can you see where this is going?

Also why are you doing this?

fThread  = new std::thread(DoReceive, (void*)this);

std::thread is not pthread_create, you don't need to pass it void*, just get rid of DoReceive and call:

fThread  = new std::thread(&SocketReceiver::Receive, this);

And why is your std::thread allocated with new? That's not necessary either, you can use a std::thread member and use joinable() to check if it's active or not, instead of checking if the pointer is non-null.

Upvotes: 4

Some programmer dude
Some programmer dude

Reputation: 409176

You're joining the thread in itself. Call join in the parent thread, like in SocketReceiver::Stop.

When join is called, it cleans up underlying data. And if you call it in the thread you're running then that data will be removed while the thread still needs it.

Upvotes: 4

Related Questions