0

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
This commit is contained in:
deanm@google.com
2008-09-03 16:47:37 +00:00
parent acc0d96fda
commit 83a05ddf63
9 changed files with 383 additions and 9 deletions

@ -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',

@ -713,6 +713,14 @@
RelativePath="..\thread_local_storage_win.cc"
>
</File>
<File
RelativePath="..\thread_local.h"
>
</File>
<File
RelativePath="..\thread_local_win.cc"
>
</File>
<File
RelativePath="..\time.cc"
>

@ -311,6 +311,10 @@
RelativePath="..\thread_local_storage_unittest.cc"
>
</File>
<File
RelativePath="..\thread_local_unittest.cc"
>
</File>
<File
RelativePath="..\thread_unittest.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;
}

@ -9,7 +9,6 @@
#include "base/message_loop.h"
#include "base/platform_thread.h"
#include "base/thread_local_storage.h"
namespace base {

121
base/thread_local.h Normal file

@ -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_

@ -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

@ -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());
}
}

38
base/thread_local_win.cc Normal file

@ -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