C++11: Nontrivial Thread Local Static Variable?

Andrew Tomazos picture Andrew Tomazos · Aug 21, 2012 · Viewed 10.8k times · Source

I have a class X:

class X { ... }

I want to do this:

void f()
{
    thread_local static X x = ...;

    ...
}

(actually I'm using gcc so keyword is "__thread")

but I can't because you can only have trivial thread_locals.

What is the best work-around for this?

If I do it this way:

void f()
{
    thread_local static X* p = 0;

    if (!p)
       p = new X(...);

    X& x = *p;

    ...
}

then:

  1. the destructor won't be called when thread exits
  2. unnecessary dynamic memory allocation.

Update:

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; } };

void f()
{
        static __thread bool x_allocated = false;
        static __thread aligned_storage<sizeof(X),
             alignment_of<X>::value>::type x_storage;

        if (!x_allocated)
        {
                new (&x_storage) X;
                x_allocated = true;
                // add thread cleanup that calls destructor
        }

        X& x = *((X*) &x_storage);
}

int main()
{
        f();
}

This fixes the dynamic memory allocation problem. I just need to add the thread cleanup handler. Is there a mechanism to do this with pthreads?

Answer

jogojapan picture jogojapan · Aug 21, 2012

The Standard describes thread_local as a storage specifier like the others (static, extern etc.) in §7.1.1. There is no restriction to "simple" data types by any definition of that word.

The problem is briefly discussed in a pre-C++11 discussion document N2147 (see the section "Thread Variable Dynamic Initialization"). That includes a description of the key problems involved in the proper implementation. Apparently the GCC implementation (static __thread) hasn't solved these problems yet (which is consistent with the fact that GCC does not officially support C++11 thread_local).

One alternative is boost::thread_specfic_ptr<> mentioned in this earlier post and described here.

Another alternative is to use a std::thread object to implement the thread and ensure each instance maintains its own copy of the variable, possibly wrapped in a unique_ptr.