Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

pthread_key_create destructor not getting called

As per pthread_key_create man page we can associate a destructor to be called at thread shut down. My problem is that the destructor function I have registered is not being called. Gist of my code is as follows.

static pthread_key_t key;
static pthread_once_t tls_init_flag = PTHREAD_ONCE_INIT;

void destructor(void *t) {
  // thread local data structure clean up code here, which is not getting called
}

void create_key() {
  pthread_key_create(&key, destructor);
}

// This will be called from every thread
void set_thread_specific() {

  ts = new ts_stack; // Thread local data structure

  pthread_once(&tls_init_flag, create_key);
  pthread_setspecific(key, ts);
}

Any idea what might prevent this destructor being called? I am also using atexit() at moment to do some cleanup in the main thread. Is there any chance that is interfering with destructor function being called? I tried removing that as well. Still didn't work though. Also I am not clear if I should handle the main thread as a separate case with atexit. (It's a must to use atexit by the way, since I need to do some application specific cleanup at application exit)

like image 637
chamibuddhika Avatar asked Jul 02 '14 02:07

chamibuddhika


2 Answers

This is by design.

The main thread exits (by returning or calling exit()), and that doesn't use pthread_exit(). POSIX documents pthread_exit calling the thread-specific destructors.

You could add pthread_exit() at the end of main. Alternatively, you can use atexit to do your destruction. In that case, it would be clean to set the thread-specific value to NULL so in case the pthread_exit was invoked, the destruction wouldn't happen twice for that key.

UPDATE Actually, I've solved my immediate worries by simply adding this to my global unit test setup function:

::atexit([] { ::pthread_exit(0); });

So, in context of my global fixture class MyConfig:

struct MyConfig {
    MyConfig()   {
        GOOGLE_PROTOBUF_VERIFY_VERSION;
        ::atexit([] { ::pthread_exit(0); });
    }
    ~MyConfig()  { google::protobuf::ShutdownProtobufLibrary(); }
};

Some of the references used:

  • http://www.resolvinghere.com/sof/6357154.shtml
  • https://sourceware.org/ml/pthreads-win32/2008/msg00007.html
  • http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.html
  • http://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_exit.html

PS. Of course c++11 introduced <thread> so you have better and more portable primitves to work with.

like image 200
sehe Avatar answered Nov 17 '22 00:11

sehe


It's already in sehe's answer, just to present the key points in a compact way:

  • pthread_key_create() destructor calls are triggered by a call to pthread_exit().
  • If the start routine of a thread returns, the behaviour is as if pthread_exit() was called (i. e., destructor calls are triggered).
  • However, if main() returns, the behaviour is as if exit() was called — no destructor calls are triggered.

This is explained in http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_create.html. See also C++17 6.6.1p5 or C11 5.1.2.2.3p1.

like image 28
Alexander Klauer Avatar answered Nov 17 '22 02:11

Alexander Klauer