The Boost C++ Libraries

Thread Local Storage

Thread Local Storage (TLS) is a dedicated storage area that can only be accessed by one thread. TLS variables can be seen as global variables that are only visible to a particular thread and not the whole program.

Example 44.12. Synchronizing multiple threads with static variables
#include <boost/thread.hpp>
#include <iostream>

boost::mutex mutex;

void init()
{
  static bool done = false;
  boost::lock_guard<boost::mutex> lock{mutex};
  if (!done)
  {
    done = true;
    std::cout << "done" << '\n';
  }
}

void thread()
{
  init();
  init();
}

int main()
{
  boost::thread t[3];

  for (int i = 0; i < 3; ++i)
    t[i] = boost::thread{thread};

  for (int i = 0; i < 3; ++i)
    t[i].join();
}

Example 44.12 executes a function thread() in three threads. thread() calls another function init() twice, and init() checks whether the boolean variable done is false. If it is, the variable is set to true and done is written to standard output.

done is a static variable that is shared by all threads. If the first thread sets done to true, the second and third thread won’t write done to standard output. The second call of init() in any thread won’t write done to standard output either. The example will print done once.

A static variable like done can be used to do a one-time initialization in a process. To do a one-time initialization per thread, TLS can be used.

Example 44.13. Synchronizing multiple threads with TLS
#include <boost/thread.hpp>
#include <iostream>

boost::mutex mutex;

void init()
{
  static boost::thread_specific_ptr<bool> tls;
  if (!tls.get())
  {
    tls.reset(new bool{true});
    boost::lock_guard<boost::mutex> lock{mutex};
    std::cout << "done" << '\n';
  }
}

void thread()
{
  init();
  init();
}

int main()
{
  boost::thread t[3];

  for (int i = 0; i < 3; ++i)
    t[i] = boost::thread{thread};

  for (int i = 0; i < 3; ++i)
    t[i].join();
}

In Example 44.13, the static variable done has been replaced with a TLS variable, tls, which is based on the class template boost::thread_specific_ptr – instantiated with the type bool. In principle, tls works like done: It acts as a condition that indicates whether or not something has already been done. The crucial difference, however, is that the value stored by tls is only visible and available to the corresponding thread.

Once a variable of type boost::thread_specific_ptr is created, it can be set. This variable expects the address of a variable of type bool instead of the variable itself. Using the reset() member function, the address can be stored in tls. In Example 44.13, a variable of type bool is dynamically allocated and its address, returned by new, is stored in tls. To avoid setting tls every time init() is called, the member function get() is used to check whether an address has already been stored.

Because boost::thread_specific_ptr stores an address, this class behaves like a pointer. For example, it provides the member functions operator* and operator->, which work as you would expect them to work with pointers.

Example 44.13 prints done to standard output three times. Each thread prints done in the first call to init(). Because a TLS variable is used, each thread uses its own variable tls. When the first thread initializes tls with a pointer to a dynamically allocated boolean variable, the tls variables in the second and third thread are still uninitialized. Since TLS variables are global per thread, not global per process, using tls in one thread does not change the variable in any other thread.