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
10using base::internal::PlatformThreadLocalStorage;
11
12namespace {
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.
21base::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.
31base::subtle::Atomic32 g_last_used_tls_key = 0;
32
33// The maximum number of 'slots' in our thread local storage stack.
34const 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.
38const 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.
49volatile 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.
58void** 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
110void 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
170namespace base {
171
172namespace internal {
173
174#if defined(OS_WIN)
175void 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)
187void PlatformThreadLocalStorage::OnThreadExit(void* value) {
188  OnThreadExitInternal(value);
189}
190#endif  // defined(OS_WIN)
191
192}  // namespace internal
193
194ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
195  initialized_ = false;
196  slot_ = 0;
197  Initialize(destructor);
198}
199
200bool 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
218void 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
228void* 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
239void 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