Home | History | Annotate | Download | only in threading
      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