diff options
-rw-r--r-- | base/SConscript | 3 | ||||
-rw-r--r-- | base/build/base.vcproj | 8 | ||||
-rw-r--r-- | base/build/base_unittests.vcproj | 4 | ||||
-rw-r--r-- | base/thread.cc | 22 | ||||
-rw-r--r-- | base/thread.h | 1 | ||||
-rw-r--r-- | base/thread_local.h | 121 | ||||
-rw-r--r-- | base/thread_local_posix.cc | 36 | ||||
-rw-r--r-- | base/thread_local_unittest.cc | 159 | ||||
-rw-r--r-- | base/thread_local_win.cc | 38 |
9 files changed, 383 insertions, 9 deletions
diff --git a/base/SConscript b/base/SConscript index a2fc266..acd5570 100644 --- a/base/SConscript +++ b/base/SConscript @@ -116,6 +116,7 @@ if env['PLATFORM'] == 'win32': 'shared_memory_win.cc', 'sys_string_conversions_win.cc', 'thread_local_storage_win.cc', + 'thread_local_win.cc', 'time_win.cc', 'waitable_event_win.cc', 'win_util.cc', @@ -133,6 +134,7 @@ if env['PLATFORM'] in ('darwin', 'posix'): 'shared_memory_posix.cc', 'string16.cc', 'thread_local_storage_posix.cc', + 'thread_local_posix.cc', 'time_posix.cc', 'waitable_event_generic.cc', ]) @@ -277,6 +279,7 @@ if env['PLATFORM'] == 'win32': 'shared_memory_unittest.cc', 'stats_table_unittest.cc', 'thread_local_storage_unittest.cc', + 'thread_local_unittest.cc', 'watchdog_unittest.cc', 'gfx/native_theme_unittest.cc', 'gfx/uniscribe_unittest.cc', diff --git a/base/build/base.vcproj b/base/build/base.vcproj index b6fa837..add54fe 100644 --- a/base/build/base.vcproj +++ b/base/build/base.vcproj @@ -714,6 +714,14 @@ > </File> <File + RelativePath="..\thread_local.h" + > + </File> + <File + RelativePath="..\thread_local_win.cc" + > + </File> + <File RelativePath="..\time.cc" > </File> diff --git a/base/build/base_unittests.vcproj b/base/build/base_unittests.vcproj index 0c22754..dbfd6a4 100644 --- a/base/build/base_unittests.vcproj +++ b/base/build/base_unittests.vcproj @@ -312,6 +312,10 @@ > </File> <File + RelativePath="..\thread_local_unittest.cc" + > + </File> + <File RelativePath="..\thread_unittest.cc" > </File> diff --git a/base/thread.cc b/base/thread.cc index 20612d4..4f68546 100644 --- a/base/thread.cc +++ b/base/thread.cc @@ -4,7 +4,9 @@ #include "base/thread.h" +#include "base/singleton.h" #include "base/string_util.h" +#include "base/thread_local.h" #include "base/waitable_event.h" namespace base { @@ -46,21 +48,25 @@ Thread::~Thread() { // because its Stop method was called. This allows us to catch cases where // MessageLoop::Quit() is called directly, which is unexpected when using a // Thread to setup and run a MessageLoop. -// Note that if we start doing complex stuff in other static initializers -// this could cause problems. -// TODO(evanm): this shouldn't rely on static initialization. -TLSSlot Thread::tls_index_; +namespace { + +// Use a differentiating type to make sure we don't share our boolean we any +// other Singleton<ThreadLocalBoolean>'s. +struct ThreadExitedDummyDiffType { }; +typedef Singleton<ThreadLocalBoolean, + DefaultSingletonTraits<ThreadLocalBoolean>, + ThreadExitedDummyDiffType> ThreadExitedSingleton; + +} // namespace void Thread::SetThreadWasQuitProperly(bool flag) { -#ifndef NDEBUG - tls_index_.Set(reinterpret_cast<void*>(flag)); -#endif + ThreadExitedSingleton::get()->Set(flag); } bool Thread::GetThreadWasQuitProperly() { bool quit_properly = true; #ifndef NDEBUG - quit_properly = (tls_index_.Get() != 0); + quit_properly = ThreadExitedSingleton::get()->Get(); #endif return quit_properly; } diff --git a/base/thread.h b/base/thread.h index 619d2b7..8d08ade 100644 --- a/base/thread.h +++ b/base/thread.h @@ -9,7 +9,6 @@ #include "base/message_loop.h" #include "base/platform_thread.h" -#include "base/thread_local_storage.h" namespace base { diff --git a/base/thread_local.h b/base/thread_local.h new file mode 100644 index 0000000..7b3c3da --- /dev/null +++ b/base/thread_local.h @@ -0,0 +1,121 @@ +// Copyright (c) 2006-2008 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. + +// WARNING: Thread local storage is a bit tricky to get right. Please make +// sure that this is really the proper solution for what you're trying to +// achieve. Don't prematurely optimize, most likely you can just use a Lock. +// +// These classes implement a warpper around the platform's TLS storage +// mechanism. On construction, they will allocate a TLS slot, and free the +// TLS slot on destruction. No memory management (creation or destruction) is +// handled. This means for uses of ThreadLocalPointer, you must correctly +// manage the memory yourself, these classes will not destroy the pointer for +// you. There are no at-thread-exit actions taken by these classes. +// +// ThreadLocalPointer<Type> wraps a Type*. It performs no creation or +// destruction, so memory management must be handled elsewhere. The first call +// to Get() on a thread will return NULL. You can update the pointer with a +// call to Set(). +// +// ThreadLocalBoolean wraps a bool. It will default to false if it has never +// been set otherwise with Set(). +// +// Thread Safety: An instance of ThreadLocalStorage is completely thread safe +// once it has been created. If you want to dynamically create an instance, +// you must of course properly deal with safety and race conditions. This +// means a function-level static initializer is generally inappropiate. +// +// Example usage: +// // My class is logically attached to a single thread. We cache a pointer +// // on the thread it was created on, so we can implement current(). +// MyClass::MyClass() { +// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() == NULL); +// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(this); +// } +// +// MyClass::~MyClass() { +// DCHECK(Singleton<ThreadLocalPointer<MyClass> >::get()->Get() != NULL); +// Singleton<ThreadLocalPointer<MyClass> >::get()->Set(NULL); +// } +// +// // Return the current MyClass associated with the calling thread, can be +// // NULL if there isn't a MyClass associated. +// MyClass* MyClass::current() { +// return Singleton<ThreadLocalPointer<MyClass> >::get()->Get(); +// } + +#ifndef BASE_THREAD_LOCAL_H_ +#define BASE_THREAD_LOCAL_H_ + +#include "base/basictypes.h" + +#if defined(OS_POSIX) +#include <pthread.h> +#endif + +namespace base { + +// Helper functions that abstract the cross-platform APIs. Do not use directly. +struct ThreadLocalPlatform { +#if defined(OS_WIN) + typedef int SlotType; +#elif defined(OS_POSIX) + typedef pthread_key_t SlotType; +#endif + + static void AllocateSlot(SlotType& slot); + static void FreeSlot(SlotType& slot); + static void* GetValueFromSlot(SlotType& slot); + static void SetValueInSlot(SlotType& slot, void* value); +}; + +template <typename Type> +class ThreadLocalPointer { + public: + ThreadLocalPointer() : slot_() { + ThreadLocalPlatform::AllocateSlot(slot_); + } + + ~ThreadLocalPointer() { + ThreadLocalPlatform::FreeSlot(slot_); + } + + Type* Get() { + return static_cast<Type*>(ThreadLocalPlatform::GetValueFromSlot(slot_)); + } + + void Set(Type* ptr) { + ThreadLocalPlatform::SetValueInSlot(slot_, ptr); + } + + private: + typedef ThreadLocalPlatform::SlotType SlotType; + + SlotType slot_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalPointer<Type>); +}; + +class ThreadLocalBoolean { + public: + ThreadLocalBoolean() { } + ~ThreadLocalBoolean() { } + + bool Get() { + return tlp_.Get() != NULL; + } + + void Set(bool val) { + tlp_.Set(reinterpret_cast<void*>(val ? 1 : 0)); + } + + private: + ThreadLocalPointer<void> tlp_; + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalBoolean); +}; + +} // namespace base + +#endif // BASE_THREAD_LOCAL_H_ diff --git a/base/thread_local_posix.cc b/base/thread_local_posix.cc new file mode 100644 index 0000000..d544069 --- /dev/null +++ b/base/thread_local_posix.cc @@ -0,0 +1,36 @@ +// Copyright (c) 2006-2008 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/thread_local.h" + +#include <pthread.h> + +#include "base/logging.h" + +namespace base { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + int error = pthread_key_create(&slot, NULL); + CHECK(error == 0); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType& slot) { + int error = pthread_key_delete(slot); + DCHECK(error == 0); +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { + return pthread_getspecific(slot_); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) { + int error = pthread_setspecific(slot, value); + CHECK(error == 0); +} + +} // namespace base diff --git a/base/thread_local_unittest.cc b/base/thread_local_unittest.cc new file mode 100644 index 0000000..9d6f875 --- /dev/null +++ b/base/thread_local_unittest.cc @@ -0,0 +1,159 @@ +// Copyright (c) 2006-2008 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/simple_thread.h" +#include "base/thread_local.h" +#include "base/waitable_event.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace { + +class ThreadLocalTesterBase : public base::DelegateSimpleThreadPool::Delegate { + public: + typedef base::ThreadLocalPointer<ThreadLocalTesterBase> TLPType; + + ThreadLocalTesterBase(TLPType* tlp, base::WaitableEvent* done) + : tlp_(tlp), done_(done) { } + ~ThreadLocalTesterBase() { } + + protected: + TLPType* tlp_; + base::WaitableEvent* done_; +}; + +class SetThreadLocal : public ThreadLocalTesterBase { + public: + SetThreadLocal(TLPType* tlp, base::WaitableEvent* done) + : ThreadLocalTesterBase(tlp, done), val_(NULL) { } + ~SetThreadLocal() { } + + void set_value(ThreadLocalTesterBase* val) { val_ = val; } + + virtual void Run() { + DCHECK(!done_->IsSignaled()); + tlp_->Set(val_); + done_->Signal(); + } + + private: + ThreadLocalTesterBase* val_; +}; + +class GetThreadLocal : public ThreadLocalTesterBase { + public: + GetThreadLocal(TLPType* tlp, base::WaitableEvent* done) + : ThreadLocalTesterBase(tlp, done), ptr_(NULL) { } + ~GetThreadLocal() { } + + void set_ptr(ThreadLocalTesterBase** ptr) { ptr_ = ptr; } + + virtual void Run() { + DCHECK(!done_->IsSignaled()); + *ptr_ = tlp_->Get(); + done_->Signal(); + } + + private: + ThreadLocalTesterBase** 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<ThreadLocalTesterBase> tlp; + + static ThreadLocalTesterBase* const kBogusPointer = + reinterpret_cast<ThreadLocalTesterBase*>(0x1234); + + ThreadLocalTesterBase* tls_val; + base::WaitableEvent done(true, false); + + 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(NULL, tls_val); + + tls_val = kBogusPointer; + done.Reset(); + tp2.AddWork(&getter); + done.Wait(); + EXPECT_EQ(NULL, 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 = NULL; + 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(NULL, tls_val); + + // Set thread 2 to kBogusPointer + 1. + setter.set_value(kBogusPointer + 1); + + done.Reset(); + tp2.AddWork(&setter); + done.Wait(); + + tls_val = NULL; + done.Reset(); + tp2.AddWork(&getter); + done.Wait(); + EXPECT_EQ(kBogusPointer + 1, tls_val); + + // Make sure thread 1 is still kBogusPointer. + tls_val = NULL; + done.Reset(); + tp1.AddWork(&getter); + done.Wait(); + EXPECT_EQ(kBogusPointer, tls_val); + + tp1.JoinAll(); + tp2.JoinAll(); +} + +TEST(ThreadLocalTest, Boolean) { + { + base::ThreadLocalBoolean tlb; + EXPECT_EQ(false, tlb.Get()); + + tlb.Set(false); + EXPECT_EQ(false, tlb.Get()); + + tlb.Set(true); + EXPECT_EQ(true, tlb.Get()); + } + + // Our slot should have been freed, we're all reset. + { + base::ThreadLocalBoolean tlb; + EXPECT_EQ(false, tlb.Get()); + } +} diff --git a/base/thread_local_win.cc b/base/thread_local_win.cc new file mode 100644 index 0000000..2da3cfd --- /dev/null +++ b/base/thread_local_win.cc @@ -0,0 +1,38 @@ +// Copyright (c) 2006-2008 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/thread_local.h" + +#include <windows.h> + +#include "base/logging.h" + +namespace base { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + slot = TlsAlloc(); + CHECK(slot != TLS_OUT_OF_INDEXES); +} + +// static +void ThreadLocalPlatform::FreeSlot(SlotType& slot) { + if (!TlsFree(slot)) { + NOTREACHED() << "Failed to deallocate tls slot with TlsFree()."; + } +} + +// static +void* ThreadLocalPlatform::GetValueFromSlot(SlotType& slot) { + return TlsGetValue(slot); +} + +// static +void ThreadLocalPlatform::SetValueInSlot(SlotType& slot, void* value) { + if (!TlsSetValue(slot, value)) { + CHECK(false) << "Failed to TlsSetValue()."; + } +} + +} // namespace base |