thread_local_storage_unittest.cc 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  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. #include "base/threading/thread_local_storage.h"
  5. #include "base/memory/raw_ptr.h"
  6. #include "build/build_config.h"
  7. #if BUILDFLAG(IS_WIN)
  8. #include <windows.h>
  9. #include <process.h>
  10. #endif
  11. #include "base/no_destructor.h"
  12. #include "base/threading/simple_thread.h"
  13. #include "build/build_config.h"
  14. #include "testing/gtest/include/gtest/gtest.h"
  15. #if BUILDFLAG(IS_WIN)
  16. // Ignore warnings about ptr->int conversions that we use when
  17. // storing ints into ThreadLocalStorage.
  18. #pragma warning(disable : 4311 4312)
  19. #endif
  20. namespace base {
  21. #if BUILDFLAG(IS_POSIX)
  22. namespace internal {
  23. // This class is friended by ThreadLocalStorage.
  24. class ThreadLocalStorageTestInternal {
  25. public:
  26. static bool HasBeenDestroyed() {
  27. return ThreadLocalStorage::HasBeenDestroyed();
  28. }
  29. };
  30. } // namespace internal
  31. #endif // BUILDFLAG(IS_POSIX)
  32. namespace {
  33. const int kInitialTlsValue = 0x5555;
  34. const int kFinalTlsValue = 0x7777;
  35. // How many times must a destructor be called before we really are done.
  36. const int kNumberDestructorCallRepetitions = 3;
  37. void ThreadLocalStorageCleanup(void* value);
  38. ThreadLocalStorage::Slot& TLSSlot() {
  39. static NoDestructor<ThreadLocalStorage::Slot> slot(
  40. &ThreadLocalStorageCleanup);
  41. return *slot;
  42. }
  43. class ThreadLocalStorageRunner : public DelegateSimpleThread::Delegate {
  44. public:
  45. explicit ThreadLocalStorageRunner(int* tls_value_ptr)
  46. : tls_value_ptr_(tls_value_ptr) {}
  47. ThreadLocalStorageRunner(const ThreadLocalStorageRunner&) = delete;
  48. ThreadLocalStorageRunner& operator=(const ThreadLocalStorageRunner&) = delete;
  49. ~ThreadLocalStorageRunner() override = default;
  50. void Run() override {
  51. *tls_value_ptr_ = kInitialTlsValue;
  52. TLSSlot().Set(tls_value_ptr_);
  53. int* ptr = static_cast<int*>(TLSSlot().Get());
  54. EXPECT_EQ(ptr, tls_value_ptr_);
  55. EXPECT_EQ(*ptr, kInitialTlsValue);
  56. *tls_value_ptr_ = 0;
  57. ptr = static_cast<int*>(TLSSlot().Get());
  58. EXPECT_EQ(ptr, tls_value_ptr_);
  59. EXPECT_EQ(*ptr, 0);
  60. *ptr = kFinalTlsValue + kNumberDestructorCallRepetitions;
  61. }
  62. private:
  63. raw_ptr<int> tls_value_ptr_;
  64. };
  65. void ThreadLocalStorageCleanup(void *value) {
  66. int *ptr = static_cast<int*>(value);
  67. // Destructors should never be called with a NULL.
  68. ASSERT_NE(nullptr, ptr);
  69. if (*ptr == kFinalTlsValue)
  70. return; // We've been called enough times.
  71. ASSERT_LT(kFinalTlsValue, *ptr);
  72. ASSERT_GE(kFinalTlsValue + kNumberDestructorCallRepetitions, *ptr);
  73. --*ptr; // Move closer to our target.
  74. // Tell tls that we're not done with this thread, and still need destruction.
  75. TLSSlot().Set(value);
  76. }
  77. #if BUILDFLAG(IS_POSIX)
  78. constexpr intptr_t kDummyValue = 0xABCD;
  79. constexpr size_t kKeyCount = 20;
  80. // The order in which pthread keys are destructed is not specified by the POSIX
  81. // specification. Hopefully, of the 20 keys we create, some of them should be
  82. // destroyed after the TLS key is destroyed.
  83. class UseTLSDuringDestructionRunner {
  84. public:
  85. UseTLSDuringDestructionRunner() = default;
  86. UseTLSDuringDestructionRunner(const UseTLSDuringDestructionRunner&) = delete;
  87. UseTLSDuringDestructionRunner& operator=(
  88. const UseTLSDuringDestructionRunner&) = delete;
  89. // The order in which pthread_key destructors are called is not well defined.
  90. // Hopefully, by creating 10 both before and after initializing TLS on the
  91. // thread, at least 1 will be called after TLS destruction.
  92. void Run() {
  93. ASSERT_FALSE(internal::ThreadLocalStorageTestInternal::HasBeenDestroyed());
  94. // Create 10 pthread keys before initializing TLS on the thread.
  95. size_t slot_index = 0;
  96. for (; slot_index < 10; ++slot_index) {
  97. CreateTlsKeyWithDestructor(slot_index);
  98. }
  99. // Initialize the Chrome TLS system. It's possible that base::Thread has
  100. // already initialized Chrome TLS, but we don't rely on that.
  101. slot_.Set(reinterpret_cast<void*>(kDummyValue));
  102. // Create 10 pthread keys after initializing TLS on the thread.
  103. for (; slot_index < kKeyCount; ++slot_index) {
  104. CreateTlsKeyWithDestructor(slot_index);
  105. }
  106. }
  107. bool teardown_works_correctly() { return teardown_works_correctly_; }
  108. private:
  109. struct TLSState {
  110. pthread_key_t key;
  111. raw_ptr<bool> teardown_works_correctly;
  112. };
  113. // The POSIX TLS destruction API takes as input a single C-function, which is
  114. // called with the current |value| of a (key, value) pair. We need this
  115. // function to do two things: set the |value| to nullptr, which requires
  116. // knowing the associated |key|, and update the |teardown_works_correctly_|
  117. // state.
  118. //
  119. // To accomplish this, we set the value to an instance of TLSState, which
  120. // contains |key| as well as a pointer to |teardown_works_correctly|.
  121. static void ThreadLocalDestructor(void* value) {
  122. TLSState* state = static_cast<TLSState*>(value);
  123. int result = pthread_setspecific(state->key, nullptr);
  124. ASSERT_EQ(result, 0);
  125. // If this path is hit, then the thread local destructor was called after
  126. // the Chrome-TLS destructor and the internal state was updated correctly.
  127. // No further checks are necessary.
  128. if (internal::ThreadLocalStorageTestInternal::HasBeenDestroyed()) {
  129. *(state->teardown_works_correctly) = true;
  130. return;
  131. }
  132. // If this path is hit, then the thread local destructor was called before
  133. // the Chrome-TLS destructor is hit. The ThreadLocalStorage::Slot should
  134. // still function correctly.
  135. ASSERT_EQ(reinterpret_cast<intptr_t>(slot_.Get()), kDummyValue);
  136. }
  137. void CreateTlsKeyWithDestructor(size_t index) {
  138. ASSERT_LT(index, kKeyCount);
  139. tls_states_[index].teardown_works_correctly = &teardown_works_correctly_;
  140. int result = pthread_key_create(
  141. &(tls_states_[index].key),
  142. UseTLSDuringDestructionRunner::ThreadLocalDestructor);
  143. ASSERT_EQ(result, 0);
  144. result = pthread_setspecific(tls_states_[index].key, &tls_states_[index]);
  145. ASSERT_EQ(result, 0);
  146. }
  147. static base::ThreadLocalStorage::Slot slot_;
  148. bool teardown_works_correctly_ = false;
  149. TLSState tls_states_[kKeyCount];
  150. };
  151. base::ThreadLocalStorage::Slot UseTLSDuringDestructionRunner::slot_;
  152. void* UseTLSTestThreadRun(void* input) {
  153. UseTLSDuringDestructionRunner* runner =
  154. static_cast<UseTLSDuringDestructionRunner*>(input);
  155. runner->Run();
  156. return nullptr;
  157. }
  158. #endif // BUILDFLAG(IS_POSIX)
  159. } // namespace
  160. TEST(ThreadLocalStorageTest, Basics) {
  161. ThreadLocalStorage::Slot slot;
  162. slot.Set(reinterpret_cast<void*>(123));
  163. int value = reinterpret_cast<intptr_t>(slot.Get());
  164. EXPECT_EQ(value, 123);
  165. }
  166. #if defined(THREAD_SANITIZER)
  167. // Do not run the test under ThreadSanitizer. Because this test iterates its
  168. // own TSD destructor for the maximum possible number of times, TSan can't jump
  169. // in after the last destructor invocation, therefore the destructor remains
  170. // unsynchronized with the following users of the same TSD slot. This results
  171. // in race reports between the destructor and functions in other tests.
  172. #define MAYBE_TLSDestructors DISABLED_TLSDestructors
  173. #else
  174. #define MAYBE_TLSDestructors TLSDestructors
  175. #endif
  176. TEST(ThreadLocalStorageTest, MAYBE_TLSDestructors) {
  177. // Create a TLS index with a destructor. Create a set of
  178. // threads that set the TLS, while the destructor cleans it up.
  179. // After the threads finish, verify that the value is cleaned up.
  180. const int kNumThreads = 5;
  181. int values[kNumThreads];
  182. ThreadLocalStorageRunner* thread_delegates[kNumThreads];
  183. DelegateSimpleThread* threads[kNumThreads];
  184. // Spawn the threads.
  185. for (int index = 0; index < kNumThreads; index++) {
  186. values[index] = kInitialTlsValue;
  187. thread_delegates[index] = new ThreadLocalStorageRunner(&values[index]);
  188. threads[index] = new DelegateSimpleThread(thread_delegates[index],
  189. "tls thread");
  190. threads[index]->Start();
  191. }
  192. // Wait for the threads to finish.
  193. for (int index = 0; index < kNumThreads; index++) {
  194. threads[index]->Join();
  195. delete threads[index];
  196. delete thread_delegates[index];
  197. // Verify that the destructor was called and that we reset.
  198. EXPECT_EQ(values[index], kFinalTlsValue);
  199. }
  200. }
  201. TEST(ThreadLocalStorageTest, TLSReclaim) {
  202. // Creates and destroys many TLS slots and ensures they all zero-inited.
  203. for (int i = 0; i < 1000; ++i) {
  204. ThreadLocalStorage::Slot slot(nullptr);
  205. EXPECT_EQ(nullptr, slot.Get());
  206. slot.Set(reinterpret_cast<void*>(0xBAADF00D));
  207. EXPECT_EQ(reinterpret_cast<void*>(0xBAADF00D), slot.Get());
  208. }
  209. }
  210. #if BUILDFLAG(IS_POSIX)
  211. // Unlike POSIX, Windows does not iterate through the OS TLS to cleanup any
  212. // values there. Instead a per-module thread destruction function is called.
  213. // However, it is not possible to perform a check after this point (as the code
  214. // is detached from the thread), so this check remains POSIX only.
  215. TEST(ThreadLocalStorageTest, UseTLSDuringDestruction) {
  216. UseTLSDuringDestructionRunner runner;
  217. pthread_t thread;
  218. int result = pthread_create(&thread, nullptr, UseTLSTestThreadRun, &runner);
  219. ASSERT_EQ(result, 0);
  220. result = pthread_join(thread, nullptr);
  221. ASSERT_EQ(result, 0);
  222. EXPECT_TRUE(runner.teardown_works_correctly());
  223. }
  224. #endif // BUILDFLAG(IS_POSIX)
  225. } // namespace base