1 // Copyright (c) 2012 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 #if defined(OS_WIN) 8 #include <windows.h> 9 #include <process.h> 10 #endif 11 12 #include "base/macros.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 defined(OS_WIN) 19 // Ignore warnings about ptr->int conversions that we use when 20 // storing ints into ThreadLocalStorage. 21 #pragma warning(disable : 4311 4312) 22 #endif 23 24 namespace base { 25 26 #if defined(OS_POSIX) 27 28 namespace internal { 29 30 // This class is friended by ThreadLocalStorage. 31 class ThreadLocalStorageTestInternal { 32 public: 33 static bool HasBeenDestroyed() { 34 return ThreadLocalStorage::HasBeenDestroyed(); 35 } 36 }; 37 38 } // namespace internal 39 40 #endif // defined(OS_POSIX) 41 42 namespace { 43 44 const int kInitialTlsValue = 0x5555; 45 const int kFinalTlsValue = 0x7777; 46 // How many times must a destructor be called before we really are done. 47 const int kNumberDestructorCallRepetitions = 3; 48 49 void ThreadLocalStorageCleanup(void* value); 50 51 ThreadLocalStorage::Slot& TLSSlot() { 52 static NoDestructor<ThreadLocalStorage::Slot> slot( 53 &ThreadLocalStorageCleanup); 54 return *slot; 55 } 56 57 class ThreadLocalStorageRunner : public DelegateSimpleThread::Delegate { 58 public: 59 explicit ThreadLocalStorageRunner(int* tls_value_ptr) 60 : tls_value_ptr_(tls_value_ptr) {} 61 62 ~ThreadLocalStorageRunner() override = default; 63 64 void Run() override { 65 *tls_value_ptr_ = kInitialTlsValue; 66 TLSSlot().Set(tls_value_ptr_); 67 68 int* ptr = static_cast<int*>(TLSSlot().Get()); 69 EXPECT_EQ(ptr, tls_value_ptr_); 70 EXPECT_EQ(*ptr, kInitialTlsValue); 71 *tls_value_ptr_ = 0; 72 73 ptr = static_cast<int*>(TLSSlot().Get()); 74 EXPECT_EQ(ptr, tls_value_ptr_); 75 EXPECT_EQ(*ptr, 0); 76 77 *ptr = kFinalTlsValue + kNumberDestructorCallRepetitions; 78 } 79 80 private: 81 int* tls_value_ptr_; 82 DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorageRunner); 83 }; 84 85 86 void ThreadLocalStorageCleanup(void *value) { 87 int *ptr = reinterpret_cast<int*>(value); 88 // Destructors should never be called with a NULL. 89 ASSERT_NE(reinterpret_cast<int*>(NULL), 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 defined(OS_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 // The order in which pthread_key destructors are called is not well defined. 111 // Hopefully, by creating 10 both before and after initializing TLS on the 112 // thread, at least 1 will be called after TLS destruction. 113 void Run() { 114 ASSERT_FALSE(internal::ThreadLocalStorageTestInternal::HasBeenDestroyed()); 115 116 // Create 10 pthread keys before initializing TLS on the thread. 117 size_t slot_index = 0; 118 for (; slot_index < 10; ++slot_index) { 119 CreateTlsKeyWithDestructor(slot_index); 120 } 121 122 // Initialize the Chrome TLS system. It's possible that base::Thread has 123 // already initialized Chrome TLS, but we don't rely on that. 124 slot_.Set(reinterpret_cast<void*>(kDummyValue)); 125 126 // Create 10 pthread keys after initializing TLS on the thread. 127 for (; slot_index < kKeyCount; ++slot_index) { 128 CreateTlsKeyWithDestructor(slot_index); 129 } 130 } 131 132 bool teardown_works_correctly() { return teardown_works_correctly_; } 133 134 private: 135 struct TLSState { 136 pthread_key_t key; 137 bool* teardown_works_correctly; 138 }; 139 140 // The POSIX TLS destruction API takes as input a single C-function, which is 141 // called with the current |value| of a (key, value) pair. We need this 142 // function to do two things: set the |value| to nullptr, which requires 143 // knowing the associated |key|, and update the |teardown_works_correctly_| 144 // state. 145 // 146 // To accomplish this, we set the value to an instance of TLSState, which 147 // contains |key| as well as a pointer to |teardown_works_correctly|. 148 static void ThreadLocalDestructor(void* value) { 149 TLSState* state = static_cast<TLSState*>(value); 150 int result = pthread_setspecific(state->key, nullptr); 151 ASSERT_EQ(result, 0); 152 153 // If this path is hit, then the thread local destructor was called after 154 // the Chrome-TLS destructor and the internal state was updated correctly. 155 // No further checks are necessary. 156 if (internal::ThreadLocalStorageTestInternal::HasBeenDestroyed()) { 157 *(state->teardown_works_correctly) = true; 158 return; 159 } 160 161 // If this path is hit, then the thread local destructor was called before 162 // the Chrome-TLS destructor is hit. The ThreadLocalStorage::Slot should 163 // still function correctly. 164 ASSERT_EQ(reinterpret_cast<intptr_t>(slot_.Get()), kDummyValue); 165 } 166 167 void CreateTlsKeyWithDestructor(size_t index) { 168 ASSERT_LT(index, kKeyCount); 169 170 tls_states_[index].teardown_works_correctly = &teardown_works_correctly_; 171 int result = pthread_key_create( 172 &(tls_states_[index].key), 173 UseTLSDuringDestructionRunner::ThreadLocalDestructor); 174 ASSERT_EQ(result, 0); 175 176 result = pthread_setspecific(tls_states_[index].key, &tls_states_[index]); 177 ASSERT_EQ(result, 0); 178 } 179 180 static base::ThreadLocalStorage::Slot slot_; 181 bool teardown_works_correctly_ = false; 182 TLSState tls_states_[kKeyCount]; 183 184 DISALLOW_COPY_AND_ASSIGN(UseTLSDuringDestructionRunner); 185 }; 186 187 base::ThreadLocalStorage::Slot UseTLSDuringDestructionRunner::slot_; 188 189 void* UseTLSTestThreadRun(void* input) { 190 UseTLSDuringDestructionRunner* runner = 191 static_cast<UseTLSDuringDestructionRunner*>(input); 192 runner->Run(); 193 return nullptr; 194 } 195 196 #endif // defined(OS_POSIX) 197 198 } // namespace 199 200 TEST(ThreadLocalStorageTest, Basics) { 201 ThreadLocalStorage::Slot slot; 202 slot.Set(reinterpret_cast<void*>(123)); 203 int value = reinterpret_cast<intptr_t>(slot.Get()); 204 EXPECT_EQ(value, 123); 205 } 206 207 #if defined(THREAD_SANITIZER) || \ 208 (defined(OS_WIN) && defined(ARCH_CPU_X86_64) && !defined(NDEBUG)) 209 // Do not run the test under ThreadSanitizer. Because this test iterates its 210 // own TSD destructor for the maximum possible number of times, TSan can't jump 211 // in after the last destructor invocation, therefore the destructor remains 212 // unsynchronized with the following users of the same TSD slot. This results 213 // in race reports between the destructor and functions in other tests. 214 // 215 // It is disabled on Win x64 with incremental linking (i.e. "Debug") pending 216 // resolution of http://crbug.com/251251. 217 #define MAYBE_TLSDestructors DISABLED_TLSDestructors 218 #else 219 #define MAYBE_TLSDestructors TLSDestructors 220 #endif 221 TEST(ThreadLocalStorageTest, MAYBE_TLSDestructors) { 222 // Create a TLS index with a destructor. Create a set of 223 // threads that set the TLS, while the destructor cleans it up. 224 // After the threads finish, verify that the value is cleaned up. 225 const int kNumThreads = 5; 226 int values[kNumThreads]; 227 ThreadLocalStorageRunner* thread_delegates[kNumThreads]; 228 DelegateSimpleThread* threads[kNumThreads]; 229 230 // Spawn the threads. 231 for (int index = 0; index < kNumThreads; index++) { 232 values[index] = kInitialTlsValue; 233 thread_delegates[index] = new ThreadLocalStorageRunner(&values[index]); 234 threads[index] = new DelegateSimpleThread(thread_delegates[index], 235 "tls thread"); 236 threads[index]->Start(); 237 } 238 239 // Wait for the threads to finish. 240 for (int index = 0; index < kNumThreads; index++) { 241 threads[index]->Join(); 242 delete threads[index]; 243 delete thread_delegates[index]; 244 245 // Verify that the destructor was called and that we reset. 246 EXPECT_EQ(values[index], kFinalTlsValue); 247 } 248 } 249 250 TEST(ThreadLocalStorageTest, TLSReclaim) { 251 // Creates and destroys many TLS slots and ensures they all zero-inited. 252 for (int i = 0; i < 1000; ++i) { 253 ThreadLocalStorage::Slot slot(nullptr); 254 EXPECT_EQ(nullptr, slot.Get()); 255 slot.Set(reinterpret_cast<void*>(0xBAADF00D)); 256 EXPECT_EQ(reinterpret_cast<void*>(0xBAADF00D), slot.Get()); 257 } 258 } 259 260 #if defined(OS_POSIX) 261 // Unlike POSIX, Windows does not iterate through the OS TLS to cleanup any 262 // values there. Instead a per-module thread destruction function is called. 263 // However, it is not possible to perform a check after this point (as the code 264 // is detached from the thread), so this check remains POSIX only. 265 TEST(ThreadLocalStorageTest, UseTLSDuringDestruction) { 266 UseTLSDuringDestructionRunner runner; 267 pthread_t thread; 268 int result = pthread_create(&thread, nullptr, UseTLSTestThreadRun, &runner); 269 ASSERT_EQ(result, 0); 270 271 result = pthread_join(thread, nullptr); 272 ASSERT_EQ(result, 0); 273 274 EXPECT_TRUE(runner.teardown_works_correctly()); 275 } 276 #endif // defined(OS_POSIX) 277 278 } // namespace base 279