Gcc 4.7 on linux pthreads is a non-trivial workaround for thread_local using __thread (no boost)

In C ++ 11, you can have a nontrivial object with thread_local storage:

class X { ... } void f() { thread_local X x = ...; ... } 

Unfortunately, this function is not yet implemented in gcc (starting from 4.7).

gcc allows you to have local stream variables, but only with trivial types.

I am looking for a workaround:

Here is what I still have:

 #include <iostream> #include <type_traits> using namespace std; class X { public: X() { cout << "X::X()" << endl; }; ~X() { cout << "X::~X()" << endl; } }; typedef aligned_storage<sizeof(X), alignment_of<X>::value>::type XStorage; inline void placement_delete_x(X* p) { p->~X(); } void f() { static __thread bool x_allocated = false; static __thread XStorage x_storage; if (!x_allocated) { new (&x_storage) X; x_allocated = true; // TODO: add thread cleanup that // calls placement_delete_x(&x_storage) } X& x = *((X*) &x_storage); } int main() { f(); } 

I need help calling placement_delete_x (& x_storage) when exiting the current thread. Is there a mechanism in pthreads and / or linux that I can use for this? Do I need to add a function pointer and parameter to some pthread cleanup stack?

Update:

I think pthread_cleanup_push might be what I want:

http://www.kernel.org/doc/man-pages/online/pages/man3/pthread_cleanup_push.3.html

Will the cleanup handler call in the right circumstances for this use?

Update 2:

It looks like boost::thread_specific_ptr eventually calls pthread_key_create with the destructor parameter, not pthread_cleanup_push - to call the tls cleanup function:

http://pubs.opengroup.org/onlinepubs/009696799/functions/pthread_key_create.html

It is not clear what the difference is between the two methods, if any.

+8
c ++ multithreading linux pthreads c ++ 11
Aug 21 2018-12-12T00:
source share
2 answers

As Mike says, pthread_cleanup_push not suitable. The correct way is to use pthread_key_create .

I implemented a small demo program to show how to do this. We implement the thread_local macro, which you use as follows:

With a real C ++ 11 function, this will be:

 void f() { thread_local X x(1,2,3); ... } 

Moreover, it is:

 void f() { thread_local (X, x, 1, 2, 3); ... } 

The difference between this and boost :: thread_specifc_ptr is that there is zero dynamic memory allocation. Everything is stored with __thread duration. It is also significantly lighter, but it is specific to gcc / linux.

Overview:

  • We used std::aligned_storage to create __thread duration space for the variable
  • In the first record from this stream, we use the new location to build the variable in the repository
  • We also __thread highlight a linked list link to a call to delete a placement
  • We use pthread_setspecific to track each chapter of the topic list.
  • The function passed to pthread_key_create scans the list causing the placement to be deleted when the thread exits.

...

 #include <iostream> #include <thread> using namespace std; static pthread_key_t key; static pthread_once_t once_control = PTHREAD_ONCE_INIT; struct destructor_list { void (*destructor)(void*); void* param; destructor_list* next; }; static void execute_destructor_list(void* v) { for (destructor_list* p = (destructor_list*) v; p != 0; p = p->next) p->destructor(p->param); } static void create_key() { pthread_key_create(&key, execute_destructor_list); } void add_destructor(destructor_list* p) { pthread_once(&once_control, create_key); p->next = (destructor_list*) pthread_getspecific(key); pthread_setspecific(key, p); } template<class T> static void placement_delete(void* t) { ((T*)t)->~T(); } #define thread_local(T, t, ...) \ T& t = *((T*) \ ({ \ typedef typename aligned_storage<sizeof(T), \ alignment_of<T>::value>::type Storage; \ static __thread bool allocated = false; \ static __thread Storage storage; \ static __thread destructor_list dlist; \ \ if (!allocated) \ { \ new (&storage) T(__VA_ARGS__); \ allocated = true; \ dlist.destructor = placement_delete<T>; \ dlist.param = &storage; \ add_destructor(&dlist); \ } \ \ &storage; \ })); class X { public: int i; X(int i_in) { i = i_in; cout << "X::X()" << endl; }; void f() { cout << "X::f()" << endl; } ~X() { cout << "X::~X() i = " << i << endl; } }; void g() { thread_local(X, x, 1234); xf(); } int main() { thread t(g); t.join(); } 

Notes:

  • You need to add error checking to every pthread_ * call. I just deleted it for exposure.
  • It uses __thread , which is a GNU extension
  • It uses an expression operator to save __thread helper variable names from the parent scope. This is also a GNU extension.
+1
Aug 21 '12 at 11:30
source share

pthread_key_create and friends are what you would like to implement thread-dependent type variables with destructors. However, as a rule, you need to manage the entire process of creating and destroying variables, and I'm not sure if you can use them in combination with __thread .

pthread_cleanup_push does not fit. It is intended to allow resource allocation if a stream exits during a (short) block of code that uses this resource; as described in the documentation you refer to, it must be mapped to pthread_cleanup_pop at the same level of this function, and the handler will not be called if the thread returns from its main function. This means that you cannot use it if you want the local thread variable to persist between function calls.

In the interest of those who have no prohibition on third-party libraries, Boost provides a convenient, portable way to manage local storage.

+3
Aug 21 2018-12-12T00:
source share



All Articles