1 // Copyright 2012 The Chromium Authors
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/memory/raw_ptr.h"
8 #include "base/no_destructor.h"
9 #include "base/threading/simple_thread.h"
10 #include "build/build_config.h"
11 #include "testing/gtest/include/gtest/gtest.h"
12
13 #if BUILDFLAG(IS_WIN)
14 #include <windows.h>
15
16 #include <process.h>
17 // Ignore warnings about ptr->int conversions that we use when
18 // storing ints into ThreadLocalStorage.
19 #pragma warning(disable : 4311 4312)
20 #endif
21
22 namespace base {
23
24 #if BUILDFLAG(IS_POSIX)
25
26 namespace internal {
27
28 // This class is friended by ThreadLocalStorage.
29 class ThreadLocalStorageTestInternal {
30 public:
HasBeenDestroyed()31 static bool HasBeenDestroyed() {
32 return ThreadLocalStorage::HasBeenDestroyed();
33 }
34 };
35
36 } // namespace internal
37
38 #endif // BUILDFLAG(IS_POSIX)
39
40 namespace {
41
42 const int kInitialTlsValue = 0x5555;
43 const int kFinalTlsValue = 0x7777;
44 // How many times must a destructor be called before we really are done.
45 const int kNumberDestructorCallRepetitions = 3;
46
47 void ThreadLocalStorageCleanup(void* value);
48
TLSSlot()49 ThreadLocalStorage::Slot& TLSSlot() {
50 static NoDestructor<ThreadLocalStorage::Slot> slot(
51 &ThreadLocalStorageCleanup);
52 return *slot;
53 }
54
55 class ThreadLocalStorageRunner : public DelegateSimpleThread::Delegate {
56 public:
ThreadLocalStorageRunner(int * tls_value_ptr)57 explicit ThreadLocalStorageRunner(int* tls_value_ptr)
58 : tls_value_ptr_(tls_value_ptr) {}
59
60 ThreadLocalStorageRunner(const ThreadLocalStorageRunner&) = delete;
61 ThreadLocalStorageRunner& operator=(const ThreadLocalStorageRunner&) = delete;
62
63 ~ThreadLocalStorageRunner() override = default;
64
Run()65 void Run() override {
66 *tls_value_ptr_ = kInitialTlsValue;
67 TLSSlot().Set(tls_value_ptr_);
68
69 int* ptr = static_cast<int*>(TLSSlot().Get());
70 EXPECT_EQ(ptr, tls_value_ptr_);
71 EXPECT_EQ(*ptr, kInitialTlsValue);
72 *tls_value_ptr_ = 0;
73
74 ptr = static_cast<int*>(TLSSlot().Get());
75 EXPECT_EQ(ptr, tls_value_ptr_);
76 EXPECT_EQ(*ptr, 0);
77
78 *ptr = kFinalTlsValue + kNumberDestructorCallRepetitions;
79 }
80
81 private:
82 raw_ptr<int> tls_value_ptr_;
83 };
84
85
ThreadLocalStorageCleanup(void * value)86 void ThreadLocalStorageCleanup(void *value) {
87 int *ptr = static_cast<int*>(value);
88 // Destructors should never be called with a NULL.
89 ASSERT_NE(nullptr, ptr);
90 if (*ptr == kFinalTlsValue)
91 return; // We've been called enough times.
92 ASSERT_LT(kFinalTlsValue, *ptr);
93 ASSERT_GE(kFinalTlsValue + kNumberDestructorCallRepetitions, *ptr);
94 --*ptr; // Move closer to our target.
95 // Tell tls that we're not done with this thread, and still need destruction.
96 TLSSlot().Set(value);
97 }
98
99 #if BUILDFLAG(IS_POSIX)
100 constexpr intptr_t kDummyValue = 0xABCD;
101 constexpr size_t kKeyCount = 20;
102
103 // The order in which pthread keys are destructed is not specified by the POSIX
104 // specification. Hopefully, of the 20 keys we create, some of them should be
105 // destroyed after the TLS key is destroyed.
106 class UseTLSDuringDestructionRunner {
107 public:
108 UseTLSDuringDestructionRunner() = default;
109
110 UseTLSDuringDestructionRunner(const UseTLSDuringDestructionRunner&) = delete;
111 UseTLSDuringDestructionRunner& operator=(
112 const UseTLSDuringDestructionRunner&) = delete;
113
114 // The order in which pthread_key destructors are called is not well defined.
115 // Hopefully, by creating 10 both before and after initializing TLS on the
116 // thread, at least 1 will be called after TLS destruction.
Run()117 void Run() {
118 ASSERT_FALSE(internal::ThreadLocalStorageTestInternal::HasBeenDestroyed());
119
120 // Create 10 pthread keys before initializing TLS on the thread.
121 size_t slot_index = 0;
122 for (; slot_index < 10; ++slot_index) {
123 CreateTlsKeyWithDestructor(slot_index);
124 }
125
126 // Initialize the Chrome TLS system. It's possible that base::Thread has
127 // already initialized Chrome TLS, but we don't rely on that.
128 slot_.Set(reinterpret_cast<void*>(kDummyValue));
129
130 // Create 10 pthread keys after initializing TLS on the thread.
131 for (; slot_index < kKeyCount; ++slot_index) {
132 CreateTlsKeyWithDestructor(slot_index);
133 }
134 }
135
teardown_works_correctly()136 bool teardown_works_correctly() { return teardown_works_correctly_; }
137
138 private:
139 struct TLSState {
140 pthread_key_t key;
141 raw_ptr<bool> teardown_works_correctly;
142 };
143
144 // The POSIX TLS destruction API takes as input a single C-function, which is
145 // called with the current |value| of a (key, value) pair. We need this
146 // function to do two things: set the |value| to nullptr, which requires
147 // knowing the associated |key|, and update the |teardown_works_correctly_|
148 // state.
149 //
150 // To accomplish this, we set the value to an instance of TLSState, which
151 // contains |key| as well as a pointer to |teardown_works_correctly|.
ThreadLocalDestructor(void * value)152 static void ThreadLocalDestructor(void* value) {
153 TLSState* state = static_cast<TLSState*>(value);
154 int result = pthread_setspecific(state->key, nullptr);
155 ASSERT_EQ(result, 0);
156
157 // If this path is hit, then the thread local destructor was called after
158 // the Chrome-TLS destructor and the internal state was updated correctly.
159 // No further checks are necessary.
160 if (internal::ThreadLocalStorageTestInternal::HasBeenDestroyed()) {
161 *(state->teardown_works_correctly) = true;
162 return;
163 }
164
165 // If this path is hit, then the thread local destructor was called before
166 // the Chrome-TLS destructor is hit. The ThreadLocalStorage::Slot should
167 // still function correctly.
168 ASSERT_EQ(reinterpret_cast<intptr_t>(slot_.Get()), kDummyValue);
169 }
170
CreateTlsKeyWithDestructor(size_t index)171 void CreateTlsKeyWithDestructor(size_t index) {
172 ASSERT_LT(index, kKeyCount);
173
174 tls_states_[index].teardown_works_correctly = &teardown_works_correctly_;
175 int result = pthread_key_create(
176 &(tls_states_[index].key),
177 UseTLSDuringDestructionRunner::ThreadLocalDestructor);
178 ASSERT_EQ(result, 0);
179
180 result = pthread_setspecific(tls_states_[index].key, &tls_states_[index]);
181 ASSERT_EQ(result, 0);
182 }
183
184 static base::ThreadLocalStorage::Slot slot_;
185 bool teardown_works_correctly_ = false;
186 TLSState tls_states_[kKeyCount];
187 };
188
189 base::ThreadLocalStorage::Slot UseTLSDuringDestructionRunner::slot_;
190
UseTLSTestThreadRun(void * input)191 void* UseTLSTestThreadRun(void* input) {
192 UseTLSDuringDestructionRunner* runner =
193 static_cast<UseTLSDuringDestructionRunner*>(input);
194 runner->Run();
195 return nullptr;
196 }
197
198 #endif // BUILDFLAG(IS_POSIX)
199
200 class TlsDestructionOrderRunner : public DelegateSimpleThread::Delegate {
201 public:
202 // The runner creates |n_slots| static slots that will be destroyed at
203 // thread exit, with |spacing| empty slots between them. This allows us to
204 // test that the destruction order is correct regardless of the actual slot
205 // indices in the global array.
TlsDestructionOrderRunner(int n_slots,int spacing)206 TlsDestructionOrderRunner(int n_slots, int spacing)
207 : n_slots_(n_slots), spacing_(spacing) {}
208
Run()209 void Run() override {
210 destructor_calls.clear();
211 for (int slot = 1; slot < n_slots_ + 1; ++slot) {
212 for (int i = 0; i < spacing_; ++i) {
213 ThreadLocalStorage::Slot empty_slot(nullptr);
214 }
215 NewStaticTLSSlot(slot);
216 }
217 }
218
219 static std::vector<int> destructor_calls;
220
221 private:
NewStaticTLSSlot(int n)222 ThreadLocalStorage::Slot& NewStaticTLSSlot(int n) {
223 NoDestructor<ThreadLocalStorage::Slot> slot(
224 &TlsDestructionOrderRunner::Destructor);
225 slot->Set(reinterpret_cast<void*>(n));
226 return *slot;
227 }
228
Destructor(void * value)229 static void Destructor(void* value) {
230 int n = reinterpret_cast<intptr_t>(value);
231 destructor_calls.push_back(n);
232 }
233
234 int n_slots_;
235 int spacing_;
236 };
237 std::vector<int> TlsDestructionOrderRunner::destructor_calls;
238
239 class CreateDuringDestructionRunner : public DelegateSimpleThread::Delegate {
240 public:
Run()241 void Run() override {
242 second_destructor_called = false;
243 NoDestructor<ThreadLocalStorage::Slot> slot(
244 &CreateDuringDestructionRunner::FirstDestructor);
245 slot->Set(reinterpret_cast<void*>(123));
246 }
247
248 static bool second_destructor_called;
249
250 private:
251 // The first destructor allocates another TLS slot, which should also be
252 // destroyed eventually.
FirstDestructor(void *)253 static void FirstDestructor(void*) {
254 NoDestructor<ThreadLocalStorage::Slot> slot(
255 &CreateDuringDestructionRunner::SecondDestructor);
256 slot->Set(reinterpret_cast<void*>(234));
257 }
258
SecondDestructor(void *)259 static void SecondDestructor(void*) { second_destructor_called = true; }
260 };
261 bool CreateDuringDestructionRunner::second_destructor_called = false;
262
263 } // namespace
264
TEST(ThreadLocalStorageTest,Basics)265 TEST(ThreadLocalStorageTest, Basics) {
266 ThreadLocalStorage::Slot slot;
267 slot.Set(reinterpret_cast<void*>(123));
268 int value = reinterpret_cast<intptr_t>(slot.Get());
269 EXPECT_EQ(value, 123);
270 }
271
272 #if defined(THREAD_SANITIZER)
273 // Do not run the test under ThreadSanitizer. Because this test iterates its
274 // own TSD destructor for the maximum possible number of times, TSan can't jump
275 // in after the last destructor invocation, therefore the destructor remains
276 // unsynchronized with the following users of the same TSD slot. This results
277 // in race reports between the destructor and functions in other tests.
278 #define MAYBE_TLSDestructors DISABLED_TLSDestructors
279 #else
280 #define MAYBE_TLSDestructors TLSDestructors
281 #endif
TEST(ThreadLocalStorageTest,MAYBE_TLSDestructors)282 TEST(ThreadLocalStorageTest, MAYBE_TLSDestructors) {
283 // Create a TLS index with a destructor. Create a set of
284 // threads that set the TLS, while the destructor cleans it up.
285 // After the threads finish, verify that the value is cleaned up.
286 const int kNumThreads = 5;
287 int values[kNumThreads];
288 ThreadLocalStorageRunner* thread_delegates[kNumThreads];
289 DelegateSimpleThread* threads[kNumThreads];
290
291 // Spawn the threads.
292 for (int index = 0; index < kNumThreads; index++) {
293 values[index] = kInitialTlsValue;
294 thread_delegates[index] = new ThreadLocalStorageRunner(&values[index]);
295 threads[index] = new DelegateSimpleThread(thread_delegates[index],
296 "tls thread");
297 threads[index]->Start();
298 }
299
300 // Wait for the threads to finish.
301 for (int index = 0; index < kNumThreads; index++) {
302 threads[index]->Join();
303 delete threads[index];
304 delete thread_delegates[index];
305
306 // Verify that the destructor was called and that we reset.
307 EXPECT_EQ(values[index], kFinalTlsValue);
308 }
309 }
310
TEST(ThreadLocalStorageTest,TLSReclaim)311 TEST(ThreadLocalStorageTest, TLSReclaim) {
312 // Creates and destroys many TLS slots and ensures they all zero-inited.
313 for (int i = 0; i < 1000; ++i) {
314 ThreadLocalStorage::Slot slot(nullptr);
315 EXPECT_EQ(nullptr, slot.Get());
316 slot.Set(reinterpret_cast<void*>(0xBAADF00D));
317 EXPECT_EQ(reinterpret_cast<void*>(0xBAADF00D), slot.Get());
318 }
319 }
320
321 #if BUILDFLAG(IS_POSIX)
322 // Unlike POSIX, Windows does not iterate through the OS TLS to cleanup any
323 // values there. Instead a per-module thread destruction function is called.
324 // However, it is not possible to perform a check after this point (as the code
325 // is detached from the thread), so this check remains POSIX only.
TEST(ThreadLocalStorageTest,UseTLSDuringDestruction)326 TEST(ThreadLocalStorageTest, UseTLSDuringDestruction) {
327 UseTLSDuringDestructionRunner runner;
328 pthread_t thread;
329 int result = pthread_create(&thread, nullptr, UseTLSTestThreadRun, &runner);
330 ASSERT_EQ(result, 0);
331
332 result = pthread_join(thread, nullptr);
333 ASSERT_EQ(result, 0);
334
335 EXPECT_TRUE(runner.teardown_works_correctly());
336 }
337 #endif // BUILDFLAG(IS_POSIX)
338
339 // Test that TLS slots are destroyed in the reverse order: the one that was
340 // created first is destroyed last.
TEST(ThreadLocalStorageTest,DestructionOrder)341 TEST(ThreadLocalStorageTest, DestructionOrder) {
342 const size_t kNSlots = 5;
343 const size_t kSpacing = 100;
344 // The total number of slots is 256, so creating 5 slots with 100 space
345 // between them will place them in different parts of the slot array.
346 // This test checks that their destruction order depends only on their
347 // creation order and not on their index in the array.
348 TlsDestructionOrderRunner runner(kNSlots, kSpacing);
349 DelegateSimpleThread thread(&runner, "tls thread");
350 thread.Start();
351 thread.Join();
352 ASSERT_EQ(kNSlots, TlsDestructionOrderRunner::destructor_calls.size());
353 for (int call = 0, slot = kNSlots; slot > 0; --slot, ++call) {
354 EXPECT_EQ(slot, TlsDestructionOrderRunner::destructor_calls[call]);
355 }
356 }
357
TEST(ThreadLocalStorageTest,CreateDuringDestruction)358 TEST(ThreadLocalStorageTest, CreateDuringDestruction) {
359 CreateDuringDestructionRunner runner;
360 DelegateSimpleThread thread(&runner, "tls thread");
361 thread.Start();
362 thread.Join();
363 ASSERT_TRUE(CreateDuringDestructionRunner::second_destructor_called);
364 }
365
366 } // namespace base
367