gcc 4.7 on linux pthreads - nontrivial thread_local workaround using __thread (no boost)

c++, c++11, linux, multithreading, pthreads

Solution

As Mike says `pthread_cleanup_push` is not appropriate. The correct way is to use `pthread_key_create`.

I've implemented a small demo program to show how to do it. We implement a macro `thread_local` that you use like this:

With the real C++11 feature it would be:

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

With this 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-weight, but it is gcc/linux specific.

Overview:

- We used `std::aligned_storage` to make __thread duration space for the variable

- On first entry from a given thread we use placement new to construct the variable in the storage

- We also `__thread` allocate a linked list entry to the placement delete call

- We use `pthread_setspecific` to keep track of each threads list head

- The function passed to `pthread_key_create` walks the list calling the placement deletes 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);
    x.f();
}

int main()
{
    thread t(g);
    t.join();
}

Notes:

- You need to add error checking to each pthread_* call. I just removed it for exposition.

- It uses `__thread` which is a GNU extension

- It uses an expression statement to keep the auxillary __thread variable names out of the parent scope. This is also a GNU extension.

Problem

In C++11 you can have a non-trivial object with thread_local storage: ``` class X { ... } void f() { thread_local X x = ...; ... } ``` Unfortunately this feature hasn't been implemented in gcc yet (as of 4.7). gcc does allow you to have thread local variables but only with trivial types. I am looking for a workaround: Here is what I have so far: ``` #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(); } ``` What I need help with is calling placement_delete_x(&x_storage) on exit of the current thread. Is there a mechanism in pthreads and/or linux I can use to do this? I would need to add a function pointer and a parameter to some sort of 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 this call the cleanup handler in the correct circumstances for this usage? Update 2: It looks like `boost::thread_specific_ptr` eventually calls `pthread_key_create` with the `destructor` parameter, and not `pthread_cleanup_push` - to call its tls cleanup function: http://pubs.opengroup.org/onlinepubs/009696799/functions/pthread_key_create.html It is unclear what the difference between these two methods is, if any. ?

Original source