diff options
author | michaelbai@chromium.org <michaelbai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-18 21:36:58 +0000 |
---|---|---|
committer | michaelbai@chromium.org <michaelbai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-18 21:36:58 +0000 |
commit | e918a7ea0fd090eacebfc2c16da3be3550e4c797 (patch) | |
tree | 13130dd0a6fff47b37ad86020084d91075d6751c /base/threading | |
parent | 2e8d5349774ea29838981d42d6a8d7f648956fdd (diff) | |
download | chromium_src-e918a7ea0fd090eacebfc2c16da3be3550e4c797.zip chromium_src-e918a7ea0fd090eacebfc2c16da3be3550e4c797.tar.gz chromium_src-e918a7ea0fd090eacebfc2c16da3be3550e4c797.tar.bz2 |
Implement chromium's TLS.
Using one system TLS to implement multiple chrome's TLS slots.
BUG=264406
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=241144
Review URL: https://codereview.chromium.org/60743004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@241657 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'base/threading')
-rw-r--r-- | base/threading/thread_local_storage.cc | 250 | ||||
-rw-r--r-- | base/threading/thread_local_storage.h | 71 | ||||
-rw-r--r-- | base/threading/thread_local_storage_posix.cc | 43 | ||||
-rw-r--r-- | base/threading/thread_local_storage_win.cc | 202 |
4 files changed, 343 insertions, 223 deletions
diff --git a/base/threading/thread_local_storage.cc b/base/threading/thread_local_storage.cc new file mode 100644 index 0000000..e5a3cd9 --- /dev/null +++ b/base/threading/thread_local_storage.cc @@ -0,0 +1,250 @@ +// Copyright 2013 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/atomicops.h" +#include "base/logging.h" + +using base::internal::PlatformThreadLocalStorage; + +namespace { +// In order to make TLS destructors work, we need to keep around a function +// pointer to the destructor for each slot. We keep this array of pointers in a +// global (static) array. +// We use the single OS-level TLS slot (giving us one pointer per thread) to +// hold a pointer to a per-thread array (table) of slots that we allocate to +// Chromium consumers. + +// g_native_tls_key is the one native TLS that we use. It stores our table. +base::subtle::AtomicWord g_native_tls_key = + PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES; + +// g_last_used_tls_key is the high-water-mark of allocated thread local storage. +// Each allocation is an index into our g_tls_destructors[]. Each such index is +// assigned to the instance variable slot_ in a ThreadLocalStorage::Slot +// instance. We reserve the value slot_ == 0 to indicate that the corresponding +// instance of ThreadLocalStorage::Slot has been freed (i.e., destructor called, +// etc.). This reserved use of 0 is then stated as the initial value of +// g_last_used_tls_key, so that the first issued index will be 1. +base::subtle::Atomic32 g_last_used_tls_key = 0; + +// The maximum number of 'slots' in our thread local storage stack. +const int kThreadLocalStorageSize = 64; + +// The maximum number of times to try to clear slots by calling destructors. +// Use pthread naming convention for clarity. +const int kMaxDestructorIterations = kThreadLocalStorageSize; + +// 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. +// The elements are volatile to ensure that when the compiler reads the value +// to potentially call the destructor, it does so once, and that value is tested +// for null-ness and then used. Yes, that would be a weird de-optimization, +// but I can imagine some register machines where it was just as easy to +// re-fetch an array element, and I want to be sure a call to free the key +// (i.e., null out the destructor entry) that happens on a separate thread can't +// hurt the racy calls to the destructors on another thread. +volatile base::ThreadLocalStorage::TLSDestructorFunc + g_tls_destructors[kThreadLocalStorageSize]; + +// This function is called to initialize our entire Chromium TLS system. +// It may be called very early, and we need to complete most all of the setup +// (initialization) before calling *any* memory allocator functions, which may +// recursively depend on this initialization. +// As a result, we use Atomics, and avoid anything (like a singleton) that might +// require memory allocations. +void** ConstructTlsVector() { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) { + CHECK(PlatformThreadLocalStorage::AllocTLS(&key)); + + // The TLS_KEY_OUT_OF_INDEXES is used to find out whether the key is set or + // not in NoBarrier_CompareAndSwap, but Posix doesn't have invalid key, we + // define an almost impossible value be it. + // If we really get TLS_KEY_OUT_OF_INDEXES as value of key, just alloc + // another TLS slot. + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) { + PlatformThreadLocalStorage::TLSKey tmp = key; + CHECK(PlatformThreadLocalStorage::AllocTLS(&key) && + key != PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES); + PlatformThreadLocalStorage::FreeTLS(tmp); + } + // Atomically test-and-set the tls_key. If the key is + // TLS_KEY_OUT_OF_INDEXES, go ahead and set it. Otherwise, do nothing, as + // another thread already did our dirty work. + if (PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES != + base::subtle::NoBarrier_CompareAndSwap(&g_native_tls_key, + PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES, key)) { + // We've been shortcut. Another thread replaced g_native_tls_key first so + // we need to destroy our index and use the one the other thread got + // first. + PlatformThreadLocalStorage::FreeTLS(key); + key = base::subtle::NoBarrier_Load(&g_native_tls_key); + } + } + CHECK(!PlatformThreadLocalStorage::GetTLSValue(key)); + + // Some allocators, such as TCMalloc, make use of thread local storage. + // As a result, any attempt to call new (or malloc) will lazily cause such a + // system to initialize, which will include registering for a TLS key. If we + // are not careful here, then that request to create a key will call new back, + // and we'll have an infinite loop. We avoid that as follows: + // Use a stack allocated vector, so that we don't have dependence on our + // allocator until our service is in place. (i.e., don't even call new until + // after we're setup) + void* stack_allocated_tls_data[kThreadLocalStorageSize]; + memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data)); + // Ensure that any rentrant calls change the temp version. + PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data); + + // Allocate an array to store our data. + void** tls_data = new void*[kThreadLocalStorageSize]; + memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data)); + PlatformThreadLocalStorage::SetTLSValue(key, tls_data); + return tls_data; +} + +void OnThreadExitInternal(void* value) { + DCHECK(value); + void** tls_data = static_cast<void**>(value); + // Some allocators, such as TCMalloc, use TLS. As a result, when a thread + // terminates, one of the destructor calls we make may be to shut down an + // allocator. We have to be careful that after we've shutdown all of the + // known destructors (perchance including an allocator), that we don't call + // the allocator and cause it to resurrect itself (with no possibly destructor + // call to follow). We handle this problem as follows: + // Switch to using a stack allocated vector, so that we don't have dependence + // on our allocator after we have called all g_tls_destructors. (i.e., don't + // even call delete[] after we're done with destructors.) + void* stack_allocated_tls_data[kThreadLocalStorageSize]; + memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data)); + // Ensure that any re-entrant calls change the temp version. + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + PlatformThreadLocalStorage::SetTLSValue(key, stack_allocated_tls_data); + delete[] tls_data; // Our last dependence on an allocator. + + int remaining_attempts = kMaxDestructorIterations; + bool need_to_scan_destructors = true; + while (need_to_scan_destructors) { + need_to_scan_destructors = false; + // Try to destroy the first-created-slot (which is slot 1) in our last + // destructor call. That user was able to function, and define a slot with + // no other services running, so perhaps it is a basic service (like an + // allocator) and should also be destroyed last. If we get the order wrong, + // then we'll itterate several more times, so it is really not that + // critical (but it might help). + base::subtle::Atomic32 last_used_tls_key = + base::subtle::NoBarrier_Load(&g_last_used_tls_key); + for (int slot = last_used_tls_key; slot > 0; --slot) { + void* value = stack_allocated_tls_data[slot]; + if (value == NULL) + continue; + + base::ThreadLocalStorage::TLSDestructorFunc destructor = + g_tls_destructors[slot]; + if (destructor == NULL) + continue; + stack_allocated_tls_data[slot] = NULL; // pre-clear the slot. + destructor(value); + // Any destructor might have called a different service, which then set + // a different slot to a non-NULL value. Hence we need to check + // the whole vector again. This is a pthread standard. + need_to_scan_destructors = true; + } + if (--remaining_attempts <= 0) { + NOTREACHED(); // Destructors might not have been called. + break; + } + } + + // Remove our stack allocated vector. + PlatformThreadLocalStorage::SetTLSValue(key, NULL); +} + +} // namespace + +namespace base { + +namespace internal { + +#if defined(OS_WIN) +void PlatformThreadLocalStorage::OnThreadExit() { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES) + return; + void *tls_data = GetTLSValue(key); + // Maybe we have never initialized TLS for this thread. + if (!tls_data) + return; + OnThreadExitInternal(tls_data); +} +#elif defined(OS_POSIX) +void PlatformThreadLocalStorage::OnThreadExit(void* value) { + OnThreadExitInternal(value); +} +#endif // defined(OS_WIN) + +} // namespace internal + +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { + initialized_ = false; + slot_ = 0; + Initialize(destructor); +} + +bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { + PlatformThreadLocalStorage::TLSKey key = + base::subtle::NoBarrier_Load(&g_native_tls_key); + if (key == PlatformThreadLocalStorage::TLS_KEY_OUT_OF_INDEXES || + !PlatformThreadLocalStorage::GetTLSValue(key)) + ConstructTlsVector(); + + // Grab a new slot. + slot_ = base::subtle::NoBarrier_AtomicIncrement(&g_last_used_tls_key, 1); + DCHECK_GT(slot_, 0); + CHECK_LT(slot_, kThreadLocalStorageSize); + + // Setup our destructor. + g_tls_destructors[slot_] = destructor; + initialized_ = true; + return true; +} + +void ThreadLocalStorage::StaticSlot::Free() { + // At this time, we don't reclaim old indices for TLS slots. + // So all we need to do is wipe the destructor. + DCHECK_GT(slot_, 0); + DCHECK_LT(slot_, kThreadLocalStorageSize); + g_tls_destructors[slot_] = NULL; + slot_ = 0; + initialized_ = false; +} + +void* ThreadLocalStorage::StaticSlot::Get() const { + void** tls_data = static_cast<void**>( + PlatformThreadLocalStorage::GetTLSValue( + base::subtle::NoBarrier_Load(&g_native_tls_key))); + if (!tls_data) + tls_data = ConstructTlsVector(); + DCHECK_GT(slot_, 0); + DCHECK_LT(slot_, kThreadLocalStorageSize); + return tls_data[slot_]; +} + +void ThreadLocalStorage::StaticSlot::Set(void* value) { + void** tls_data = static_cast<void**>( + PlatformThreadLocalStorage::GetTLSValue( + base::subtle::NoBarrier_Load(&g_native_tls_key))); + if (!tls_data) + tls_data = ConstructTlsVector(); + DCHECK_GT(slot_, 0); + DCHECK_LT(slot_, kThreadLocalStorageSize); + tls_data[slot_] = value; +} + +} // namespace base diff --git a/base/threading/thread_local_storage.h b/base/threading/thread_local_storage.h index eb5648f..53ebe55 100644 --- a/base/threading/thread_local_storage.h +++ b/base/threading/thread_local_storage.h @@ -8,12 +8,71 @@ #include "base/base_export.h" #include "base/basictypes.h" -#if defined(OS_POSIX) +#if defined(OS_WIN) +#include <windows.h> +#elif defined(OS_POSIX) #include <pthread.h> #endif namespace base { +namespace internal { + +// WARNING: You should *NOT* be using this class directly. +// PlatformThreadLocalStorage is low-level abstraction to the OS's TLS +// interface, you should instead be using ThreadLocalStorage::StaticSlot/Slot. +class BASE_EXPORT PlatformThreadLocalStorage { + public: + +#if defined(OS_WIN) + typedef unsigned long TLSKey; + enum { TLS_KEY_OUT_OF_INDEXES = TLS_OUT_OF_INDEXES }; +#elif defined(OS_POSIX) + typedef pthread_key_t TLSKey; + // The following is a "reserved key" which is used in our generic Chromium + // ThreadLocalStorage implementation. We expect that an OS will not return + // such a key, but if it is returned (i.e., the OS tries to allocate it) we + // will just request another key. + enum { TLS_KEY_OUT_OF_INDEXES = 0x7FFFFFFF }; +#endif + + // The following methods need to be supported on each OS platform, so that + // the Chromium ThreadLocalStore functionality can be constructed. + // Chromium will use these methods to acquire a single OS slot, and then use + // that to support a much larger number of Chromium slots (independent of the + // OS restrictions). + // The following returns true if it successfully is able to return an OS + // key in |key|. + static bool AllocTLS(TLSKey* key); + // Note: FreeTLS() doesn't have to be called, it is fine with this leak, OS + // might not reuse released slot, you might just reset the TLS value with + // SetTLSValue(). + static void FreeTLS(TLSKey key); + static void SetTLSValue(TLSKey key, void* value); + static void* GetTLSValue(TLSKey key); + + // Each platform (OS implementation) is required to call this method on each + // terminating thread when the thread is about to terminate. This method + // will then call all registered destructors for slots in Chromium + // ThreadLocalStorage, until there are no slot values remaining as having + // been set on this thread. + // Destructors may end up being called multiple times on a terminating + // thread, as other destructors may re-set slots that were previously + // destroyed. +#if defined(OS_WIN) + // Since Windows which doesn't support TLS destructor, the implementation + // should use GetTLSValue() to retrieve the value of TLS slot. + static void OnThreadExit(); +#elif defined(OS_POSIX) + // |Value| is the data stored in TLS slot, The implementation can't use + // GetTLSValue() to retrieve the value of slot as it has already been reset + // in Posix. + static void OnThreadExit(void* value); +#endif +}; + +} // namespace internal + // Wrapper for thread local storage. This class doesn't do much except provide // an API for portability. class BASE_EXPORT ThreadLocalStorage { @@ -60,12 +119,7 @@ class BASE_EXPORT ThreadLocalStorage { // 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 - }; // A convenience wrapper around StaticSlot with a constructor. Can be used @@ -77,11 +131,8 @@ class BASE_EXPORT ThreadLocalStorage { private: using StaticSlot::initialized_; -#if defined(OS_WIN) using StaticSlot::slot_; -#elif defined(OS_POSIX) - using StaticSlot::key_; -#endif + DISALLOW_COPY_AND_ASSIGN(Slot); }; diff --git a/base/threading/thread_local_storage_posix.cc b/base/threading/thread_local_storage_posix.cc index 75da5a7..ebaf400 100644 --- a/base/threading/thread_local_storage_posix.cc +++ b/base/threading/thread_local_storage_posix.cc @@ -8,42 +8,27 @@ namespace base { -ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { - initialized_ = false; - key_ = 0; - Initialize(destructor); -} - -bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { - DCHECK(!initialized_); - int error = pthread_key_create(&key_, destructor); - if (error) { - NOTREACHED(); - return false; - } +namespace internal { - initialized_ = true; - return true; +bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) { + return !pthread_key_create(key, + base::internal::PlatformThreadLocalStorage::OnThreadExit); } -void ThreadLocalStorage::StaticSlot::Free() { - DCHECK(initialized_); - int error = pthread_key_delete(key_); - if (error) - NOTREACHED(); - initialized_ = false; +void PlatformThreadLocalStorage::FreeTLS(TLSKey key) { + int ret = pthread_key_delete(key); + DCHECK_EQ(ret, 0); } -void* ThreadLocalStorage::StaticSlot::Get() const { - DCHECK(initialized_); - return pthread_getspecific(key_); +void* PlatformThreadLocalStorage::GetTLSValue(TLSKey key) { + return pthread_getspecific(key); } -void ThreadLocalStorage::StaticSlot::Set(void* value) { - DCHECK(initialized_); - int error = pthread_setspecific(key_, value); - if (error) - NOTREACHED(); +void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) { + int ret = pthread_setspecific(key, value); + DCHECK_EQ(ret, 0); } +} // namespace internal + } // namespace base diff --git a/base/threading/thread_local_storage_win.cc b/base/threading/thread_local_storage_win.cc index 0ae3cb4..42a7d01 100644 --- a/base/threading/thread_local_storage_win.cc +++ b/base/threading/thread_local_storage_win.cc @@ -8,201 +8,35 @@ #include "base/logging.h" - -namespace { -// 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. - -// g_native_tls_key is the one native TLS that we use. It stores our table. -long g_native_tls_key = TLS_OUT_OF_INDEXES; - -// g_last_used_tls_key is the high-water-mark of allocated thread local storage. -// Each allocation is an index into our g_tls_destructors[]. Each such index is -// assigned to the instance variable slot_ in a ThreadLocalStorage::Slot -// instance. We reserve the value slot_ == 0 to indicate that the corresponding -// instance of ThreadLocalStorage::Slot has been freed (i.e., destructor called, -// etc.). This reserved use of 0 is then stated as the initial value of -// g_last_used_tls_key, so that the first issued index will be 1. -long g_last_used_tls_key = 0; - -// The maximum number of 'slots' in our thread local storage stack. -const int kThreadLocalStorageSize = 64; - -// The maximum number of times to try to clear slots by calling destructors. -// Use pthread naming convention for clarity. -const int kMaxDestructorIterations = kThreadLocalStorageSize; - -// 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. -// The elements are volatile to ensure that when the compiler reads the value -// to potentially call the destructor, it does so once, and that value is tested -// for null-ness and then used. Yes, that would be a weird de-optimization, -// but I can imagine some register machines where it was just as easy to -// re-fetch an array element, and I want to be sure a call to free the key -// (i.e., null out the destructor entry) that happens on a separate thread can't -// hurt the racy calls to the destructors on another thread. -volatile base::ThreadLocalStorage::TLSDestructorFunc - g_tls_destructors[kThreadLocalStorageSize]; - -void** ConstructTlsVector() { - if (g_native_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 (TLS_OUT_OF_INDEXES != InterlockedCompareExchange( - &g_native_tls_key, value, TLS_OUT_OF_INDEXES)) { - // We've been shortcut. Another thread replaced g_native_tls_key first so - // we need to destroy our index and use the one the other thread got - // first. - TlsFree(value); - } - } - DCHECK(!TlsGetValue(g_native_tls_key)); - - // Some allocators, such as TCMalloc, make use of thread local storage. - // As a result, any attempt to call new (or malloc) will lazily cause such a - // system to initialize, which will include registering for a TLS key. If we - // are not careful here, then that request to create a key will call new back, - // and we'll have an infinite loop. We avoid that as follows: - // Use a stack allocated vector, so that we don't have dependence on our - // allocator until our service is in place. (i.e., don't even call new until - // after we're setup) - void* stack_allocated_tls_data[kThreadLocalStorageSize]; - memset(stack_allocated_tls_data, 0, sizeof(stack_allocated_tls_data)); - // Ensure that any rentrant calls change the temp version. - TlsSetValue(g_native_tls_key, stack_allocated_tls_data); - - // Allocate an array to store our data. - void** tls_data = new void*[kThreadLocalStorageSize]; - memcpy(tls_data, stack_allocated_tls_data, sizeof(stack_allocated_tls_data)); - TlsSetValue(g_native_tls_key, tls_data); - return tls_data; -} - -// Called when we terminate a thread, this function calls any TLS destructors -// that are pending for this thread. -void WinThreadExit() { - if (g_native_tls_key == TLS_OUT_OF_INDEXES) - return; - - void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key)); - // Maybe we have never initialized TLS for this thread. - if (!tls_data) - return; - - // Some allocators, such as TCMalloc, use TLS. As a result, when a thread - // terminates, one of the destructor calls we make may be to shut down an - // allocator. We have to be careful that after we've shutdown all of the - // known destructors (perchance including an allocator), that we don't call - // the allocator and cause it to resurrect itself (with no possibly destructor - // call to follow). We handle this problem as follows: - // Switch to using a stack allocated vector, so that we don't have dependence - // on our allocator after we have called all g_tls_destructors. (i.e., don't - // even call delete[] after we're done with destructors.) - void* stack_allocated_tls_data[kThreadLocalStorageSize]; - memcpy(stack_allocated_tls_data, tls_data, sizeof(stack_allocated_tls_data)); - // Ensure that any re-entrant calls change the temp version. - TlsSetValue(g_native_tls_key, stack_allocated_tls_data); - delete[] tls_data; // Our last dependence on an allocator. - - int remaining_attempts = kMaxDestructorIterations; - bool need_to_scan_destructors = true; - while (need_to_scan_destructors) { - need_to_scan_destructors = false; - // Try to destroy the first-created-slot (which is slot 1) in our last - // destructor call. That user was able to function, and define a slot with - // no other services running, so perhaps it is a basic service (like an - // allocator) and should also be destroyed last. If we get the order wrong, - // then we'll itterate several more times, so it is really not that - // critical (but it might help). - for (int slot = g_last_used_tls_key; slot > 0; --slot) { - void* value = stack_allocated_tls_data[slot]; - if (value == NULL) - continue; - base::ThreadLocalStorage::TLSDestructorFunc destructor = - g_tls_destructors[slot]; - if (destructor == NULL) - continue; - stack_allocated_tls_data[slot] = NULL; // pre-clear the slot. - destructor(value); - // Any destructor might have called a different service, which then set - // a different slot to a non-NULL value. Hence we need to check - // the whole vector again. This is a pthread standard. - need_to_scan_destructors = true; - } - if (--remaining_attempts <= 0) { - NOTREACHED(); // Destructors might not have been called. - break; - } - } - - // Remove our stack allocated vector. - TlsSetValue(g_native_tls_key, NULL); -} - -} // namespace - namespace base { -ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { - initialized_ = false; - slot_ = 0; - Initialize(destructor); -} - -bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { - if (g_native_tls_key == TLS_OUT_OF_INDEXES || !TlsGetValue(g_native_tls_key)) - ConstructTlsVector(); +namespace internal { - // Grab a new slot. - slot_ = InterlockedIncrement(&g_last_used_tls_key); - DCHECK_GT(slot_, 0); - if (slot_ >= kThreadLocalStorageSize) { - NOTREACHED(); - return false; +bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) { + TLSKey value = TlsAlloc(); + if (value != TLS_OUT_OF_INDEXES) { + *key = value; + return true; } - - // Setup our destructor. - g_tls_destructors[slot_] = destructor; - initialized_ = true; - return true; + return false; } -void ThreadLocalStorage::StaticSlot::Free() { - // At this time, we don't reclaim old indices for TLS slots. - // So all we need to do is wipe the destructor. - DCHECK_GT(slot_, 0); - DCHECK_LT(slot_, kThreadLocalStorageSize); - g_tls_destructors[slot_] = NULL; - slot_ = 0; - initialized_ = false; +void PlatformThreadLocalStorage::FreeTLS(TLSKey key) { + BOOL ret = TlsFree(key); + DCHECK(ret); } -void* ThreadLocalStorage::StaticSlot::Get() const { - void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key)); - if (!tls_data) - tls_data = ConstructTlsVector(); - DCHECK_GT(slot_, 0); - DCHECK_LT(slot_, kThreadLocalStorageSize); - return tls_data[slot_]; +void* PlatformThreadLocalStorage::GetTLSValue(TLSKey key) { + return TlsGetValue(key); } -void ThreadLocalStorage::StaticSlot::Set(void* value) { - void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key)); - if (!tls_data) - tls_data = ConstructTlsVector(); - DCHECK_GT(slot_, 0); - DCHECK_LT(slot_, kThreadLocalStorageSize); - tls_data[slot_] = value; +void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) { + BOOL ret = TlsSetValue(key, value); + DCHECK(ret); } +} // namespace internal + } // namespace base // Thread Termination Callbacks. @@ -233,7 +67,7 @@ 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) - WinThreadExit(); + base::internal::PlatformThreadLocalStorage::OnThreadExit(); } // .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are |