diff options
author | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-30 22:18:56 +0000 |
---|---|---|
committer | brettw@chromium.org <brettw@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-12-30 22:18:56 +0000 |
commit | 1357c32474af4190d22ccca26bf414443252784b (patch) | |
tree | 620927f5a079c5850e59d9774f4747b76d60ed11 /base/threading | |
parent | b99603ceb9cbec4a3b1ca9ac535f6b1cbc951bb8 (diff) | |
download | chromium_src-1357c32474af4190d22ccca26bf414443252784b.zip chromium_src-1357c32474af4190d22ccca26bf414443252784b.tar.gz chromium_src-1357c32474af4190d22ccca26bf414443252784b.tar.bz2 |
Move thread local stuff from base to base/threading and consistently use the
base namespace. This also fixes references to TLSSlot to
base::ThreadLocalStorage::Slot.
TEST=it compiles
BUG=none
Review URL: http://codereview.chromium.org/5986012
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70322 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/threading')
-rw-r--r-- | base/threading/thread_local.h | 127 | ||||
-rw-r--r-- | base/threading/thread_local_posix.cc | 40 | ||||
-rw-r--r-- | base/threading/thread_local_storage.h | 95 | ||||
-rw-r--r-- | base/threading/thread_local_storage_posix.cc | 48 | ||||
-rw-r--r-- | base/threading/thread_local_storage_unittest.cc | 100 | ||||
-rw-r--r-- | base/threading/thread_local_storage_win.cc | 198 | ||||
-rw-r--r-- | base/threading/thread_local_unittest.cc | 163 | ||||
-rw-r--r-- | base/threading/thread_local_win.cc | 42 |
8 files changed, 813 insertions, 0 deletions
diff --git a/base/threading/thread_local.h b/base/threading/thread_local.h new file mode 100644 index 0000000..069543f --- /dev/null +++ b/base/threading/thread_local.h @@ -0,0 +1,127 @@ +// Copyright (c) 2010 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 wrapper 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_THREADING_THREAD_LOCAL_H_ +#define BASE_THREADING_THREAD_LOCAL_H_ +#pragma once + +#include "base/basictypes.h" + +#if defined(OS_POSIX) +#include <pthread.h> +#endif + +namespace base { + +namespace internal { + +// Helper functions that abstract the cross-platform APIs. Do not use directly. +struct ThreadLocalPlatform { +#if defined(OS_WIN) + typedef unsigned long 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); +}; + +} // namespace internal + +template <typename Type> +class ThreadLocalPointer { + public: + ThreadLocalPointer() : slot_() { + internal::ThreadLocalPlatform::AllocateSlot(slot_); + } + + ~ThreadLocalPointer() { + internal::ThreadLocalPlatform::FreeSlot(slot_); + } + + Type* Get() { + return static_cast<Type*>( + internal::ThreadLocalPlatform::GetValueFromSlot(slot_)); + } + + void Set(Type* ptr) { + internal::ThreadLocalPlatform::SetValueInSlot(slot_, ptr); + } + + private: + typedef internal::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_THREADING_THREAD_LOCAL_H_ diff --git a/base/threading/thread_local_posix.cc b/base/threading/thread_local_posix.cc new file mode 100644 index 0000000..568fa4b --- /dev/null +++ b/base/threading/thread_local_posix.cc @@ -0,0 +1,40 @@ +// Copyright (c) 2010 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/threading/thread_local.h" + +#include <pthread.h> + +#include "base/logging.h" + +namespace base { + +namespace internal { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + int error = pthread_key_create(&slot, NULL); + CHECK_EQ(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_EQ(error, 0); +} + +} // namespace internal + +} // namespace base diff --git a/base/threading/thread_local_storage.h b/base/threading/thread_local_storage.h new file mode 100644 index 0000000..204b6530 --- /dev/null +++ b/base/threading/thread_local_storage.h @@ -0,0 +1,95 @@ +// 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. + +#ifndef BASE_THREADING_THREAD_LOCAL_STORAGE_H_ +#define BASE_THREADING_THREAD_LOCAL_STORAGE_H_ +#pragma once + +#include "base/basictypes.h" + +#if defined(OS_POSIX) +#include <pthread.h> +#endif + +namespace base { + +// Wrapper for thread local storage. This class doesn't do much except provide +// an API for portability. +class ThreadLocalStorage { + public: + + // Prototype for the TLS destructor function, which can be optionally used to + // cleanup thread local storage on thread exit. 'value' is the data that is + // stored in thread local storage. + typedef void (*TLSDestructorFunc)(void* value); + + // A key representing one value stored in TLS. + class Slot { + public: + explicit Slot(TLSDestructorFunc destructor = NULL); + + // This constructor should be used for statics. + // It returns an uninitialized Slot. + explicit Slot(base::LinkerInitialized x) {} + + // Set up the TLS slot. Called by the constructor. + // 'destructor' is a pointer to a function to perform per-thread cleanup of + // this object. If set to NULL, no cleanup is done for this TLS slot. + // Returns false on error. + bool Initialize(TLSDestructorFunc destructor); + + // Free a previously allocated TLS 'slot'. + // If a destructor was set for this slot, removes + // the destructor so that remaining threads exiting + // will not free data. + void Free(); + + // Get the thread-local value stored in slot 'slot'. + // Values are guaranteed to initially be zero. + void* Get() const; + + // Set the thread-local value stored in slot 'slot' to + // value 'value'. + void Set(void* value); + + bool initialized() const { return initialized_; } + + private: + // The internals of this struct should be considered private. + bool initialized_; +#if defined(OS_WIN) + int slot_; +#elif defined(OS_POSIX) + pthread_key_t key_; +#endif + + DISALLOW_COPY_AND_ASSIGN(Slot); + }; + +#if defined(OS_WIN) + // Function called when on thread exit to call TLS + // destructor functions. This function is used internally. + static void ThreadExit(); + + private: + // Function to lazily initialize our thread local storage. + static void **Initialize(); + + private: + // The maximum number of 'slots' in our thread local storage stack. + // For now, this is fixed. We could either increase statically, or + // we could make it dynamic in the future. + static const int kThreadLocalStorageSize = 64; + + static long tls_key_; + static long tls_max_; + static TLSDestructorFunc tls_destructors_[kThreadLocalStorageSize]; +#endif // OS_WIN + + DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage); +}; + +} // namespace base + +#endif // BASE_THREADING_THREAD_LOCAL_STORAGE_H_ diff --git a/base/threading/thread_local_storage_posix.cc b/base/threading/thread_local_storage_posix.cc new file mode 100644 index 0000000..81b7332 --- /dev/null +++ b/base/threading/thread_local_storage_posix.cc @@ -0,0 +1,48 @@ +// Copyright (c) 2010 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/threading/thread_local_storage.h" + +#include "base/logging.h" + +namespace base { + +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) + : initialized_(false) { + Initialize(destructor); +} + +bool ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) { + DCHECK(!initialized_); + int error = pthread_key_create(&key_, destructor); + if (error) { + NOTREACHED(); + return false; + } + + initialized_ = true; + return true; +} + +void ThreadLocalStorage::Slot::Free() { + DCHECK(initialized_); + int error = pthread_key_delete(key_); + if (error) + NOTREACHED(); + initialized_ = false; +} + +void* ThreadLocalStorage::Slot::Get() const { + DCHECK(initialized_); + return pthread_getspecific(key_); +} + +void ThreadLocalStorage::Slot::Set(void* value) { + DCHECK(initialized_); + int error = pthread_setspecific(key_, value); + if (error) + NOTREACHED(); +} + +} // namespace base diff --git a/base/threading/thread_local_storage_unittest.cc b/base/threading/thread_local_storage_unittest.cc new file mode 100644 index 0000000..216e650 --- /dev/null +++ b/base/threading/thread_local_storage_unittest.cc @@ -0,0 +1,100 @@ +// Copyright (c) 2010 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. + +#if defined(OS_WIN) +#include <windows.h> +#include <process.h> +#endif + +#include "base/threading/simple_thread.h" +#include "base/threading/thread_local_storage.h" +#include "testing/gtest/include/gtest/gtest.h" + +#if defined(OS_WIN) +// Ignore warnings about ptr->int conversions that we use when +// storing ints into ThreadLocalStorage. +#pragma warning(disable : 4311 4312) +#endif + +namespace base { + +namespace { + +const int kInitialTlsValue = 0x5555; +static ThreadLocalStorage::Slot tls_slot(LINKER_INITIALIZED); + +class ThreadLocalStorageRunner : public DelegateSimpleThread::Delegate { + public: + explicit ThreadLocalStorageRunner(int* tls_value_ptr) + : tls_value_ptr_(tls_value_ptr) {} + + virtual ~ThreadLocalStorageRunner() {} + + virtual void Run() { + *tls_value_ptr_ = kInitialTlsValue; + tls_slot.Set(tls_value_ptr_); + + int *ptr = static_cast<int*>(tls_slot.Get()); + EXPECT_EQ(ptr, tls_value_ptr_); + EXPECT_EQ(*ptr, kInitialTlsValue); + *tls_value_ptr_ = 0; + + ptr = static_cast<int*>(tls_slot.Get()); + EXPECT_EQ(ptr, tls_value_ptr_); + EXPECT_EQ(*ptr, 0); + } + + private: + int* tls_value_ptr_; + DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorageRunner); +}; + + +void ThreadLocalStorageCleanup(void *value) { + int *ptr = reinterpret_cast<int*>(value); + if (ptr) + *ptr = kInitialTlsValue; +} + +} // namespace + +TEST(ThreadLocalStorageTest, Basics) { + ThreadLocalStorage::Slot slot; + slot.Set(reinterpret_cast<void*>(123)); + int value = reinterpret_cast<intptr_t>(slot.Get()); + EXPECT_EQ(value, 123); +} + +TEST(ThreadLocalStorageTest, TLSDestructors) { + // Create a TLS index with a destructor. Create a set of + // threads that set the TLS, while the destructor cleans it up. + // After the threads finish, verify that the value is cleaned up. + const int kNumThreads = 5; + int values[kNumThreads]; + ThreadLocalStorageRunner* thread_delegates[kNumThreads]; + DelegateSimpleThread* threads[kNumThreads]; + + tls_slot.Initialize(ThreadLocalStorageCleanup); + + // Spawn the threads. + for (int index = 0; index < kNumThreads; index++) { + values[index] = kInitialTlsValue; + thread_delegates[index] = new ThreadLocalStorageRunner(&values[index]); + threads[index] = new DelegateSimpleThread(thread_delegates[index], + "tls thread"); + threads[index]->Start(); + } + + // Wait for the threads to finish. + for (int index = 0; index < kNumThreads; index++) { + threads[index]->Join(); + delete threads[index]; + delete thread_delegates[index]; + + // Verify that the destructor was called and that we reset. + EXPECT_EQ(values[index], kInitialTlsValue); + } +} + +} // namespace base diff --git a/base/threading/thread_local_storage_win.cc b/base/threading/thread_local_storage_win.cc new file mode 100644 index 0000000..2967a27 --- /dev/null +++ b/base/threading/thread_local_storage_win.cc @@ -0,0 +1,198 @@ +// Copyright (c) 2010 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/threading/thread_local_storage.h" + +#include <windows.h> + +#include "base/logging.h" + +namespace base { + +// In order to make TLS destructors work, we need to keep function +// pointers to the destructor for each TLS that we allocate. +// We make this work by allocating a single OS-level TLS, which +// contains an array of slots for the application to use. In +// parallel, we also allocate an array of destructors, which we +// keep track of and call when threads terminate. + +// tls_key_ is the one native TLS that we use. It stores our +// table. +long ThreadLocalStorage::tls_key_ = TLS_OUT_OF_INDEXES; + +// tls_max_ is the high-water-mark of allocated thread local storage. +// We intentionally skip 0 so that it is not confused with an +// unallocated TLS slot. +long ThreadLocalStorage::tls_max_ = 1; + +// An array of destructor function pointers for the slots. If +// a slot has a destructor, it will be stored in its corresponding +// entry in this array. +ThreadLocalStorage::TLSDestructorFunc + ThreadLocalStorage::tls_destructors_[kThreadLocalStorageSize]; + +void** ThreadLocalStorage::Initialize() { + if (tls_key_ == TLS_OUT_OF_INDEXES) { + long value = TlsAlloc(); + DCHECK(value != TLS_OUT_OF_INDEXES); + + // Atomically test-and-set the tls_key. If the key is TLS_OUT_OF_INDEXES, + // go ahead and set it. Otherwise, do nothing, as another + // thread already did our dirty work. + if (InterlockedCompareExchange(&tls_key_, value, TLS_OUT_OF_INDEXES) != + TLS_OUT_OF_INDEXES) { + // We've been shortcut. Another thread replaced tls_key_ first so we need + // to destroy our index and use the one the other thread got first. + TlsFree(value); + } + } + DCHECK(TlsGetValue(tls_key_) == NULL); + + // Create an array to store our data. + void** tls_data = new void*[kThreadLocalStorageSize]; + memset(tls_data, 0, sizeof(void*[kThreadLocalStorageSize])); + TlsSetValue(tls_key_, tls_data); + return tls_data; +} + +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) + : initialized_(false) { + Initialize(destructor); +} + +bool ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) { + if (tls_key_ == TLS_OUT_OF_INDEXES || !TlsGetValue(tls_key_)) + ThreadLocalStorage::Initialize(); + + // Grab a new slot. + slot_ = InterlockedIncrement(&tls_max_) - 1; + if (slot_ >= kThreadLocalStorageSize) { + NOTREACHED(); + return false; + } + + // Setup our destructor. + tls_destructors_[slot_] = destructor; + initialized_ = true; + return true; +} + +void ThreadLocalStorage::Slot::Free() { + // At this time, we don't reclaim old indices for TLS slots. + // So all we need to do is wipe the destructor. + tls_destructors_[slot_] = NULL; + initialized_ = false; +} + +void* ThreadLocalStorage::Slot::Get() const { + void** tls_data = static_cast<void**>(TlsGetValue(tls_key_)); + if (!tls_data) + tls_data = ThreadLocalStorage::Initialize(); + DCHECK(slot_ >= 0 && slot_ < kThreadLocalStorageSize); + return tls_data[slot_]; +} + +void ThreadLocalStorage::Slot::Set(void* value) { + void** tls_data = static_cast<void**>(TlsGetValue(tls_key_)); + if (!tls_data) + tls_data = ThreadLocalStorage::Initialize(); + DCHECK(slot_ >= 0 && slot_ < kThreadLocalStorageSize); + tls_data[slot_] = value; +} + +void ThreadLocalStorage::ThreadExit() { + if (tls_key_ == TLS_OUT_OF_INDEXES) + return; + + void** tls_data = static_cast<void**>(TlsGetValue(tls_key_)); + + // Maybe we have never initialized TLS for this thread. + if (!tls_data) + return; + + for (int slot = 0; slot < tls_max_; slot++) { + if (tls_destructors_[slot] != NULL) { + void* value = tls_data[slot]; + tls_destructors_[slot](value); + } + } + + delete[] tls_data; + + // In case there are other "onexit" handlers... + TlsSetValue(tls_key_, NULL); +} + +} // namespace base + +// Thread Termination Callbacks. +// Windows doesn't support a per-thread destructor with its +// TLS primitives. So, we build it manually by inserting a +// function to be called on each thread's exit. +// This magic is from http://www.codeproject.com/threads/tls.asp +// and it works for VC++ 7.0 and later. + +// Force a reference to _tls_used to make the linker create the TLS directory +// if it's not already there. (e.g. if __declspec(thread) is not used). +// Force a reference to p_thread_callback_base to prevent whole program +// optimization from discarding the variable. +#ifdef _WIN64 + +#pragma comment(linker, "/INCLUDE:_tls_used") +#pragma comment(linker, "/INCLUDE:p_thread_callback_base") + +#else // _WIN64 + +#pragma comment(linker, "/INCLUDE:__tls_used") +#pragma comment(linker, "/INCLUDE:_p_thread_callback_base") + +#endif // _WIN64 + +// Static callback function to call with each thread termination. +void NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) { + // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+ + // and on W2K and W2K3. So don't assume it is sent. + if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason) + base::ThreadLocalStorage::ThreadExit(); +} + +// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are +// called automatically by the OS loader code (not the CRT) when the module is +// loaded and on thread creation. They are NOT called if the module has been +// loaded by a LoadLibrary() call. It must have implicitly been loaded at +// process startup. +// By implicitly loaded, I mean that it is directly referenced by the main EXE +// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being +// implicitly loaded. +// +// See VC\crt\src\tlssup.c for reference. + +// extern "C" suppresses C++ name mangling so we know the symbol name for the +// linker /INCLUDE:symbol pragma above. +extern "C" { +// The linker must not discard p_thread_callback_base. (We force a reference +// to this variable with a linker /INCLUDE:symbol pragma to ensure that.) If +// this variable is discarded, the OnThreadExit function will never be called. +#ifdef _WIN64 + +// .CRT section is merged with .rdata on x64 so it must be constant data. +#pragma const_seg(".CRT$XLB") +// When defining a const variable, it must have external linkage to be sure the +// linker doesn't discard it. +extern const PIMAGE_TLS_CALLBACK p_thread_callback_base; +const PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit; + +// Reset the default section. +#pragma const_seg() + +#else // _WIN64 + +#pragma data_seg(".CRT$XLB") +PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit; + +// Reset the default section. +#pragma data_seg() + +#endif // _WIN64 +} // extern "C" diff --git a/base/threading/thread_local_unittest.cc b/base/threading/thread_local_unittest.cc new file mode 100644 index 0000000..f6bdd78 --- /dev/null +++ b/base/threading/thread_local_unittest.cc @@ -0,0 +1,163 @@ +// Copyright (c) 2010 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/waitable_event.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace base { + +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(static_cast<ThreadLocalTesterBase*>(NULL), tls_val); + + tls_val = kBogusPointer; + done.Reset(); + tp2.AddWork(&getter); + done.Wait(); + EXPECT_EQ(static_cast<ThreadLocalTesterBase*>(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(static_cast<ThreadLocalTesterBase*>(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_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 diff --git a/base/threading/thread_local_win.cc b/base/threading/thread_local_win.cc new file mode 100644 index 0000000..56d3a3a --- /dev/null +++ b/base/threading/thread_local_win.cc @@ -0,0 +1,42 @@ +// Copyright (c) 2010 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/threading/thread_local.h" + +#include <windows.h> + +#include "base/logging.h" + +namespace base { + +namespace internal { + +// static +void ThreadLocalPlatform::AllocateSlot(SlotType& slot) { + slot = TlsAlloc(); + CHECK_NE(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)) { + LOG(FATAL) << "Failed to TlsSetValue()."; + } +} + +} // namespace internal + +} // namespace base |