summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordeanm@google.com <deanm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-09-03 16:47:37 +0000
committerdeanm@google.com <deanm@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-09-03 16:47:37 +0000
commit83a05ddf63cc5920ad04b0a6936fb8f5625daa07 (patch)
tree117361a500928d197eb8e919e8356906e6276c43
parentacc0d96fda8b5389f30bf47ca297ca8755f9778c (diff)
downloadchromium_src-83a05ddf63cc5920ad04b0a6936fb8f5625daa07.zip
chromium_src-83a05ddf63cc5920ad04b0a6936fb8f5625daa07.tar.gz
chromium_src-83a05ddf63cc5920ad04b0a6936fb8f5625daa07.tar.bz2
Add ThreadLocalPointer and ThreadLocalBoolean abstractions, that will deprecate the old ThreadLocalStorage / TLSSlot APIs.
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@1678 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--base/SConscript3
-rw-r--r--base/build/base.vcproj8
-rw-r--r--base/build/base_unittests.vcproj4
-rw-r--r--base/thread.cc22
-rw-r--r--base/thread.h1
-rw-r--r--base/thread_local.h121
-rw-r--r--base/thread_local_posix.cc36
-rw-r--r--base/thread_local_unittest.cc159
-rw-r--r--base/thread_local_win.cc38
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