Failed to call pthread_key_create destructor

According to the pthread_key_create man page, we can link the destructor that gets called when the thread is disconnected. My problem is that the destructor function that I registered is not called. The essence 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 get in the way of the name of this destructor? I also use atexit () at the moment to do some cleanup in the main thread. Is there any chance that prevents calling the function of the destructor? I also tried to remove this. Nevertheless, it still did not work out. It is also unclear whether I should treat the main thread as a separate case with atexit. (You need to use atexit, by the way, since I need to do some cleanup of the application when the application exits)

+7
source share
6 answers

This is by design.

The main thread terminates (by returning or calling exit() ) and does not use pthread_exit() . POSIX documents pthread_exit calling thread specific destructors.

You can add pthread_exit() at the end of main . Alternatively, you can use atexit to destroy. In this case, it would be clean to set the thread-dependent value to NULL , so if pthread_exit called, it will not be destroyed twice for this key.

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

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

So, in the context of my global fixture MyConfig class:

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

Some used links:


PS. Of course, C ++ 11 introduced <thread> so that you have better and more portable primitives to work with.

+3
source

This is already answered, just to present key points in a compact way:

  • pthread_key_create() calls to the destructor are called by pthread_exit() .
  • If the initial thread procedure returns, the behavior looks as if pthread_exit() were called (i.e., calls to the destructor are called).
  • However, if main() returns, the behavior looks like exit() was called - no calls to the destructor are called.

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

+1
source

I wrote a quick test, and the only thing I changed was to move the create_key call outside of set_thread_specific .

That is, I called it in the main stream.

Then I saw that my call to destroy was called when the thread procedure completed.

0
source

I call destructor () manually at the end of main () :

 void * ThreadData = NULL; if ((ThreadData = pthread_getspecific(key)) != NULL) destructor(ThreadData); 

Of course, the key must be correctly initialized earlier in main () . PS. The call to Pthread_Exit () at the end of main () seems to have hung the whole application ...

0
source

Your initial thought of treating the main thread as a separate case with atexit is best for me.

Be sure pthread_exit (0) overwrites the value of the process output. For example, the following program will exit with a status of zero, although main () returns with number three:

 #include <pthread.h> #include <stdio.h> #include <stdlib.h> class ts_stack { public: ts_stack () { printf ("init\n"); } ~ts_stack () { printf ("done\n"); } }; static void cleanup (void); 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 delete (ts_stack*) t; } void create_key() { pthread_key_create(&key, destructor); atexit(cleanup); } // This will be called from every thread void set_thread_specific() { ts_stack *ts = new ts_stack (); // Thread local data structure pthread_once(&tls_init_flag, create_key); pthread_setspecific(key, ts); } static void cleanup (void) { pthread_exit(0); // <-- Calls destructor but sets exit status to zero as a side effect! } int main (int argc, char *argv[]) { set_thread_specific(); return 3; // Attempt to exit with status of 3 } 
0
source

I had the same problem as yours: pthread_setspecific sets the key, but the destructor is never called. To fix this, we simply switched to thread_local in C ++. You can also do something like this if this change is too complicated:

For example, suppose you have some ThreadData class that you want to perform some action when the thread completes. You define a destructor for something in these lines:

 void destroy_my_data(ThreadlData* t) { delete t; } 

When your thread starts, you allocate memory for the ThreadData* instance and assign it a destructor as follows:

 ThreadData* my_data = new ThreadData; thread_local ThreadLocalDestructor<ThreadData> tld; tld.SetDestructorData(my_data, destroy_my_data); pthread_setspecific(key, my_data) 

Note that ThreadLocalDestructor defined as thread_local. We rely on the C ++ 11 mechanism, which, when exiting the stream, automatically calls the ThreadLocalDestructor destructor, and ~ThreadLocalDestructor is implemented to call the destroy_my_data function.

Here is the implementation of ThreadLocalDestructor:

 template <typename T> class ThreadLocalDestructor { public: ThreadLocalDestructor() : m_destr_func(nullptr), m_destr_data(nullptr) { } ~ThreadLocalDestructor() { if (m_destr_func) { m_destr_func(m_destr_data); } } void SetDestructorData(void (*destr_func)(T*), T* destr_data) { m_destr_data = destr_data; m_destr_func = destr_func; } private: void (*m_destr_func)(T*); T* m_destr_data; }; 
0
source

Source: https://habr.com/ru/post/971633/


All Articles