| // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "base/logging.h" | |
| #include "base/threading/simple_thread.h" | |
| #include "base/threading/thread_local.h" | |
| #include "base/synchronization/waitable_event.h" | |
| #include "testing/gtest/include/gtest/gtest.h" | |
| namespace base { | |
| namespace { | |
| class ThreadLocalTesterBase : public base::DelegateSimpleThreadPool::Delegate { | |
| public: | |
| typedef base::ThreadLocalPointer<char> TLPType; | |
| ThreadLocalTesterBase(TLPType* tlp, base::WaitableEvent* done) | |
| : tlp_(tlp), | |
| done_(done) { | |
| } | |
| ~ThreadLocalTesterBase() override = default; | |
| protected: | |
| TLPType* tlp_; | |
| base::WaitableEvent* done_; | |
| }; | |
| class SetThreadLocal : public ThreadLocalTesterBase { | |
| public: | |
| SetThreadLocal(TLPType* tlp, base::WaitableEvent* done) | |
| : ThreadLocalTesterBase(tlp, done), val_(nullptr) {} | |
| ~SetThreadLocal() override = default; | |
| void set_value(char* val) { val_ = val; } | |
| void Run() override { | |
| DCHECK(!done_->IsSignaled()); | |
| tlp_->Set(val_); | |
| done_->Signal(); | |
| } | |
| private: | |
| char* val_; | |
| }; | |
| class GetThreadLocal : public ThreadLocalTesterBase { | |
| public: | |
| GetThreadLocal(TLPType* tlp, base::WaitableEvent* done) | |
| : ThreadLocalTesterBase(tlp, done), ptr_(nullptr) {} | |
| ~GetThreadLocal() override = default; | |
| void set_ptr(char** ptr) { ptr_ = ptr; } | |
| void Run() override { | |
| DCHECK(!done_->IsSignaled()); | |
| *ptr_ = tlp_->Get(); | |
| done_->Signal(); | |
| } | |
| private: | |
| char** ptr_; | |
| }; | |
| } // namespace | |
| // In this test, we start 2 threads which will access a ThreadLocalPointer. We | |
| // make sure the default is NULL, and the pointers are unique to the threads. | |
| TEST(ThreadLocalTest, Pointer) { | |
| base::DelegateSimpleThreadPool tp1("ThreadLocalTest tp1", 1); | |
| base::DelegateSimpleThreadPool tp2("ThreadLocalTest tp1", 1); | |
| tp1.Start(); | |
| tp2.Start(); | |
| base::ThreadLocalPointer<char> tlp; | |
| static char* const kBogusPointer = reinterpret_cast<char*>(0x1234); | |
| char* tls_val; | |
| base::WaitableEvent done(WaitableEvent::ResetPolicy::MANUAL, | |
| WaitableEvent::InitialState::NOT_SIGNALED); | |
| GetThreadLocal getter(&tlp, &done); | |
| getter.set_ptr(&tls_val); | |
| // Check that both threads defaulted to NULL. | |
| tls_val = kBogusPointer; | |
| done.Reset(); | |
| tp1.AddWork(&getter); | |
| done.Wait(); | |
| EXPECT_EQ(static_cast<char*>(nullptr), tls_val); | |
| tls_val = kBogusPointer; | |
| done.Reset(); | |
| tp2.AddWork(&getter); | |
| done.Wait(); | |
| EXPECT_EQ(static_cast<char*>(nullptr), tls_val); | |
| SetThreadLocal setter(&tlp, &done); | |
| setter.set_value(kBogusPointer); | |
| // Have thread 1 set their pointer value to kBogusPointer. | |
| done.Reset(); | |
| tp1.AddWork(&setter); | |
| done.Wait(); | |
| tls_val = nullptr; | |
| done.Reset(); | |
| tp1.AddWork(&getter); | |
| done.Wait(); | |
| EXPECT_EQ(kBogusPointer, tls_val); | |
| // Make sure thread 2 is still NULL | |
| tls_val = kBogusPointer; | |
| done.Reset(); | |
| tp2.AddWork(&getter); | |
| done.Wait(); | |
| EXPECT_EQ(static_cast<char*>(nullptr), tls_val); | |
| // Set thread 2 to kBogusPointer + 1. | |
| setter.set_value(kBogusPointer + 1); | |
| done.Reset(); | |
| tp2.AddWork(&setter); | |
| done.Wait(); | |
| tls_val = nullptr; | |
| done.Reset(); | |
| tp2.AddWork(&getter); | |
| done.Wait(); | |
| EXPECT_EQ(kBogusPointer + 1, tls_val); | |
| // Make sure thread 1 is still kBogusPointer. | |
| tls_val = nullptr; | |
| done.Reset(); | |
| tp1.AddWork(&getter); | |
| done.Wait(); | |
| EXPECT_EQ(kBogusPointer, tls_val); | |
| tp1.JoinAll(); | |
| tp2.JoinAll(); | |
| } | |
| TEST(ThreadLocalTest, Boolean) { | |
| { | |
| base::ThreadLocalBoolean tlb; | |
| EXPECT_FALSE(tlb.Get()); | |
| tlb.Set(false); | |
| EXPECT_FALSE(tlb.Get()); | |
| tlb.Set(true); | |
| EXPECT_TRUE(tlb.Get()); | |
| } | |
| // Our slot should have been freed, we're all reset. | |
| { | |
| base::ThreadLocalBoolean tlb; | |
| EXPECT_FALSE(tlb.Get()); | |
| } | |
| } | |
| } // namespace base |