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