Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dynamic TLS in C++11

I'm writing a C++11 class Foo, and I want to give each instance its own thread-local storage of type Bar. That is to say, I want one Bar to be allocated per thread and per Foo instance.

If I were using pthreads, Foo would have a nonstatic member of type pthread_key_t, which Foo's constructor would initialize with pthread_key_create() and Foo's destructor would free with pthread_key_delete(). Or if I were writing for Microsoft Windows only, I could do something similar with TlsAlloc() and TlsFree(). Or if I were using Boost.Thread, Foo would have a nonstatic member of type boost::thread_specific_ptr.

In reality, however, I am trying to write portable C++11. C++11's thread_local keyword does not apply to nonstatic data members. So it's fine if you want one Bar per thread, but not if you want one Bar per thread per Foo.

So as far as I can tell, I need to define a thread-local map from Foos to Bars, and then deal with the question of how to clean up appropriately whenever a Foo is destroyed. But before I undertake that, I'm posting here in the hope that someone will stop me and say "There's an easier way."

(Btw, the reason I'm not using either pthread_key_create() or boost::thread_specific_ptr is because, if I understand correctly, they assume that all threads will be spawned using pthreads or Boost.Thread respectively. I don't want to make any assumptions about how the users of my code will spawn threads.)

like image 869
slyqualin Avatar asked Nov 01 '22 03:11

slyqualin


1 Answers

You would like Foo to contain a thread_local variable of type Bar. Since, as noted, thread_local cannot apply to a data member, we have to do something more indirect. The underlying behavior will be for N instances of Bar to exist for each instance of Foo, where N is the number of threads in existence.

Here is a somewhat inefficient way of doing it. With more code, it could be made faster. Basically, each Foo will contain a TLS map.

#include <unordered_map>

class Bar { ... };

class Foo {
private:
  static thread_local std::unordered_map<Foo*, Bar> tls;
public:    
  // All internal member functions must use this too.
  Bar *get_bar() {
    auto I = tls.find(this);
    if (I != tls.end())
      return &I->second;
    auto II = tls.emplace(this, Bar()); // Could use std::piecewise_construct here...
    return &II->second.second;
  }
};
like image 101
jared_schmitz Avatar answered Nov 04 '22 07:11

jared_schmitz