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