• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
ConstructTlsVector()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 
OnThreadExitInternal(void * value)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)
OnThreadExit()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 
Slot(TLSDestructorFunc destructor)197 ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) {
198   slot_ = 0;
199   base::subtle::Release_Store(&initialized_, 0);
200   Initialize(destructor);
201 }
202 
Initialize(TLSDestructorFunc destructor)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 
Free()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 
Get() const230 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 
Set(void * value)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