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:
@ -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
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_
|
36
base/thread_local_posix.cc
Normal file
36
base/thread_local_posix.cc
Normal file
@ -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
|
159
base/thread_local_unittest.cc
Normal file
159
base/thread_local_unittest.cc
Normal file
@ -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
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
|
Reference in New Issue
Block a user