thread_local_storage.cc revision cce46a0c214b37e8da48c522c83037e8ffa4f9fd
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
11using base::internal::PlatformThreadLocalStorage;
12
13namespace {
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.
22base::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.
32base::subtle::Atomic32 g_last_used_tls_key = 0;
33
34// The maximum number of 'slots' in our thread local storage stack.
35const 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.
39const 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.
50volatile 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.
59void** 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
113void 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
173namespace base {
174
175namespace internal {
176
177#if defined(OS_WIN)
178void 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)
190void PlatformThreadLocalStorage::OnThreadExit(void* value) {
191  OnThreadExitInternal(value);
192}
193#endif  // defined(OS_WIN)
194
195}  // namespace internal
196
197ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
198  slot_ = 0;
199  base::subtle::Release_Store(&initialized_, 0);
200  Initialize(destructor);
201}
202
203void 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
220void 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
230void* 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
241void 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