• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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