Home | History | Annotate | Download | only in threading
      1 // Copyright 2014 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "base/threading/thread_local_storage.h"
      6 
      7 #include "base/atomicops.h"
      8 #include "base/logging.h"
      9 #include "build/build_config.h"
     10 
     11 using base::internal::PlatformThreadLocalStorage;
     12 
     13 namespace {
     14 // In order to make TLS destructors work, we need to keep around a function
     15 // pointer to the destructor for each slot. We keep this array of pointers in a
     16 // global (static) array.
     17 // We use the single OS-level TLS slot (giving us one pointer per thread) to
     18 // hold a pointer to a per-thread array (table) of slots that we allocate to
     19 // Chromium consumers.
     20 
     21 // g_native_tls_key is the one native TLS that we use.  It stores our table.
     22 base::subtle::Atomic32 g_native_tls_key =
     23     PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES;
     24 
     25 // g_last_used_tls_key is the high-water-mark of allocated thread local storage.
     26 // Each allocation is an index into our g_tls_destructors[].  Each such index is
     27 // assigned to the instance variable slot_ in a ThreadLocalStorage::Slot
     28 // instance.  We reserve the value slot_ == 0 to indicate that the corresponding
     29 // instance of ThreadLocalStorage::Slot has been freed (i.e., destructor called,
     30 // etc.).  This reserved use of 0 is then stated as the initial value of
     31 // g_last_used_tls_key, so that the first issued index will be 1.
     32 base::subtle::Atomic32 g_last_used_tls_key = 0;
     33 
     34 // The maximum number of 'slots' in our thread local storage stack.
     35 const int kThreadLocalStorageSize = 256;
     36 
     37 // The maximum number of times to try to clear slots by calling destructors.
     38 // Use pthread naming convention for clarity.
     39 const int kMaxDestructorIterations = kThreadLocalStorageSize;
     40 
     41 // An array of destructor function pointers for the slots.  If a slot has a
     42 // destructor, it will be stored in its corresponding entry in this array.
     43 // The elements are volatile to ensure that when the compiler reads the value
     44 // to potentially call the destructor, it does so once, and that value is tested
     45 // for null-ness and then used. Yes, that would be a weird de-optimization,
     46 // but I can imagine some register machines where it was just as easy to
     47 // re-fetch an array element, and I want to be sure a call to free the key
     48 // (i.e., null out the destructor entry) that happens on a separate thread can't
     49 // hurt the racy calls to the destructors on another thread.
     50 volatile base::ThreadLocalStorage::TLSDestructorFunc
     51     g_tls_destructors[kThreadLocalStorageSize];
     52 
     53 // This function is called to initialize our entire Chromium TLS system.
     54 // It may be called very early, and we need to complete most all of the setup
     55 // (initialization) before calling *any* memory allocator functions, which may
     56 // recursively depend on this initialization.
     57 // As a result, we use Atomics, and avoid anything (like a singleton) that might
     58 // require memory allocations.
     59 void** ConstructTlsVector() {
     60   PlatformThreadLocalStorage::TLSKey key =
     61       base::subtle::NoBarrier_Load(&g_native_tls_key);
     62   if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) {
     63     CHECK(PlatformThreadLocalStorage::AllocTLS(&key));
     64 
     65     // The TLS_KEY_OUT_OF_INDEXES is used to find out whether the key is set or
     66     // not in NoBarrier_CompareAndSwap, but Posix doesn't have invalid key, we
     67     // define an almost impossible value be it.
     68     // If we really get TLS_KEY_OUT_OF_INDEXES as value of key, just alloc
     69     // another TLS slot.
     70     if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) {
     71       PlatformThreadLocalStorage::TLSKey tmp = key;
     72       CHECK(PlatformThreadLocalStorage::AllocTLS(&key) &&
     73             key != PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES);
     74       PlatformThreadLocalStorage::FreeTLS(tmp);
     75     }
     76     // Atomically test-and-set the tls_key.  If the key is
     77     // TLS_KEY_OUT_OF_INDEXES, go ahead and set it.  Otherwise, do nothing, as
     78     // another thread already did our dirty work.
     79     if (PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES !=
     80         static_cast<PlatformThreadLocalStorage::TLSKey>(
     81             base::subtle::NoBarrier_CompareAndSwap(
     82                 &g_native_tls_key,
     83                 PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key))) {
     84       // We've been shortcut. Another thread replaced g_native_tls_key first so
     85       // we need to destroy our index and use the one the other thread got
     86       // first.
     87       PlatformThreadLocalStorage::FreeTLS(key);
     88       key = base::subtle::NoBarrier_Load(&g_native_tls_key);
     89     }
     90   }
     91   CHECK(!PlatformThreadLocalStorage::GetTLSValue(key));
     92 
     93   // Some allocators, such as TCMalloc, make use of thread local storage.
     94   // As a result, any attempt to call new (or malloc) will lazily cause such a
     95   // system to initialize, which will include registering for a TLS key.  If we
     96   // are not careful here, then that request to create a key will call new back,
     97   // and we'll have an infinite loop.  We avoid that as follows:
     98   // Use a stack allocated vector, so that we don't have dependence on our
     99   // allocator until our service is in place.  (i.e., don't even call new until
    100   // after we're setup)
    101   void* stack_allocated_tls_data[kThreadLocalStorageSize];
    102   memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data));
    103   // Ensure that any rentrant calls change the temp version.
    104   PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data);
    105 
    106   // Allocate an array to store our data.
    107   void** tls_data = new void*[kThreadLocalStorageSize];
    108   memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data));
    109   PlatformThreadLocalStorage::SetTLSValue(key, tls_data);
    110   return tls_data;
    111 }
    112 
    113 void OnThreadExitInternal(void* value) {
    114   DCHECK(value);
    115   void** tls_data = static_cast<void**>(value);
    116   // Some allocators, such as TCMalloc, use TLS.  As a result, when a thread
    117   // terminates, one of the destructor calls we make may be to shut down an
    118   // allocator.  We have to be careful that after we've shutdown all of the
    119   // known destructors (perchance including an allocator), that we don't call
    120   // the allocator and cause it to resurrect itself (with no possibly destructor
    121   // call to follow).  We handle this problem as follows:
    122   // Switch to using a stack allocated vector, so that we don't have dependence
    123   // on our allocator after we have called all g_tls_destructors.  (i.e., don't
    124   // even call delete[] after we're done with destructors.)
    125   void* stack_allocated_tls_data[kThreadLocalStorageSize];
    126   memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data));
    127   // Ensure that any re-entrant calls change the temp version.
    128   PlatformThreadLocalStorage::TLSKey key =
    129       base::subtle::NoBarrier_Load(&g_native_tls_key);
    130   PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data);
    131   delete[] tls_data;  // Our last dependence on an allocator.
    132 
    133   int remaining_attempts = kMaxDestructorIterations;
    134   bool need_to_scan_destructors = true;
    135   while (need_to_scan_destructors) {
    136     need_to_scan_destructors = false;
    137     // Try to destroy the first-created-slot (which is slot 1) in our last
    138     // destructor call.  That user was able to function, and define a slot with
    139     // no other services running, so perhaps it is a basic service (like an
    140     // allocator) and should also be destroyed last.  If we get the order wrong,
    141     // then we'll itterate several more times, so it is really not that
    142     // critical (but it might help).
    143     base::subtle::Atomic32 last_used_tls_key =
    144         base::subtle::NoBarrier_Load(&g_last_used_tls_key);
    145     for (int slot = last_used_tls_key; slot > 0; --slot) {
    146       void* tls_value = stack_allocated_tls_data[slot];
    147       if (tls_value == NULL)
    148         continue;
    149 
    150       base::ThreadLocalStorage::TLSDestructorFunc destructor =
    151           g_tls_destructors[slot];
    152       if (destructor == NULL)
    153         continue;
    154       stack_allocated_tls_data[slot] = NULL;  // pre-clear the slot.
    155       destructor(tls_value);
    156       // Any destructor might have called a different service, which then set
    157       // a different slot to a non-NULL value.  Hence we need to check
    158       // the whole vector again.  This is a pthread standard.
    159       need_to_scan_destructors = true;
    160     }
    161     if (--remaining_attempts <= 0) {
    162       NOTREACHED();  // Destructors might not have been called.
    163       break;
    164     }
    165   }
    166 
    167   // Remove our stack allocated vector.
    168   PlatformThreadLocalStorage::SetTLSValue(key, NULL);
    169 }
    170 
    171 }  // namespace
    172 
    173 namespace base {
    174 
    175 namespace internal {
    176 
    177 #if defined(OS_WIN)
    178 void PlatformThreadLocalStorage::OnThreadExit() {
    179   PlatformThreadLocalStorage::TLSKey key =
    180       base::subtle::NoBarrier_Load(&g_native_tls_key);
    181   if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES)
    182     return;
    183   void *tls_data = GetTLSValue(key);
    184   // Maybe we have never initialized TLS for this thread.
    185   if (!tls_data)
    186     return;
    187   OnThreadExitInternal(tls_data);
    188 }
    189 #elif defined(OS_POSIX)
    190 void PlatformThreadLocalStorage::OnThreadExit(void* value) {
    191   OnThreadExitInternal(value);
    192 }
    193 #endif  // defined(OS_WIN)
    194 
    195 }  // namespace internal
    196 
    197 ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
    198   slot_ = 0;
    199   base::subtle::Release_Store(&initialized_, 0);
    200   Initialize(destructor);
    201 }
    202 
    203 void ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) {
    204   PlatformThreadLocalStorage::TLSKey key =
    205       base::subtle::NoBarrier_Load(&g_native_tls_key);
    206   if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES ||
    207       !PlatformThreadLocalStorage::GetTLSValue(key))
    208     ConstructTlsVector();
    209 
    210   // Grab a new slot.
    211   slot_ = base::subtle::NoBarrier_AtomicIncrement(&g_last_used_tls_key, 1);
    212   DCHECK_GT(slot_, 0);
    213   CHECK_LT(slot_, kThreadLocalStorageSize);
    214 
    215   // Setup our destructor.
    216   g_tls_destructors[slot_] = destructor;
    217   base::subtle::Release_Store(&initialized_, 1);
    218 }
    219 
    220 void ThreadLocalStorage::StaticSlot::Free() {
    221   // At this time, we don't reclaim old indices for TLS slots.
    222   // So all we need to do is wipe the destructor.
    223   DCHECK_GT(slot_, 0);
    224   DCHECK_LT(slot_, kThreadLocalStorageSize);
    225   g_tls_destructors[slot_] = NULL;
    226   slot_ = 0;
    227   base::subtle::Release_Store(&initialized_, 0);
    228 }
    229 
    230 void* ThreadLocalStorage::StaticSlot::Get() const {
    231   void** tls_data = static_cast<void**>(
    232       PlatformThreadLocalStorage::GetTLSValue(
    233           base::subtle::NoBarrier_Load(&g_native_tls_key)));
    234   if (!tls_data)
    235     tls_data = ConstructTlsVector();
    236   DCHECK_GT(slot_, 0);
    237   DCHECK_LT(slot_, kThreadLocalStorageSize);
    238   return tls_data[slot_];
    239 }
    240 
    241 void ThreadLocalStorage::StaticSlot::Set(void* value) {
    242   void** tls_data = static_cast<void**>(
    243       PlatformThreadLocalStorage::GetTLSValue(
    244           base::subtle::NoBarrier_Load(&g_native_tls_key)));
    245   if (!tls_data)
    246     tls_data = ConstructTlsVector();
    247   DCHECK_GT(slot_, 0);
    248   DCHECK_LT(slot_, kThreadLocalStorageSize);
    249   tls_data[slot_] = value;
    250 }
    251 
    252 }  // namespace base
    253