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