diff options
author | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-26 20:05:11 +0000 |
---|---|---|
committer | kbr@chromium.org <kbr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-12-26 20:05:11 +0000 |
commit | 1598afceb93db0ee4cd4131b33c1fcdac2f28b79 (patch) | |
tree | 7708714aca2968a3bf354923f579940938bc58d4 /base/threading | |
parent | bb2392ce9145707c6255abfc807a40db0526b708 (diff) | |
download | chromium_src-1598afceb93db0ee4cd4131b33c1fcdac2f28b79.zip chromium_src-1598afceb93db0ee4cd4131b33c1fcdac2f28b79.tar.gz chromium_src-1598afceb93db0ee4cd4131b33c1fcdac2f28b79.tar.bz2 |
Revert 241657 "Implement chromium's TLS."
Caused intermittent browser process crashes on Linux Debug configurations.
See Issue 329747 for details.
BUG=329747,264406
> 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
TBR=michaelbai@chromium.org
Review URL: https://codereview.chromium.org/121393002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@242549 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, 223 insertions, 343 deletions
diff --git a/base/threading/thread_local_storage.cc b/base/threading/thread_local_storage.cc deleted file mode 100644 index e5a3cd9..0000000 --- a/base/threading/thread_local_storage.cc +++ /dev/null @@ -1,250 +0,0 @@ -// 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 53ebe55..eb5648f 100644 --- a/base/threading/thread_local_storage.h +++ b/base/threading/thread_local_storage.h @@ -8,71 +8,12 @@ #include "base/base_export.h" #include "base/basictypes.h" -#if defined(OS_WIN) -#include <windows.h> -#elif defined(OS_POSIX) +#if 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 { @@ -119,7 +60,12 @@ 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 @@ -131,8 +77,11 @@ 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 ebaf400..75da5a7 100644 --- a/base/threading/thread_local_storage_posix.cc +++ b/base/threading/thread_local_storage_posix.cc @@ -8,27 +8,42 @@ namespace base { -namespace internal { - -bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) { - return !pthread_key_create(key, - base::internal::PlatformThreadLocalStorage::OnThreadExit); +ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { + initialized_ = false; + key_ = 0; + Initialize(destructor); } -void PlatformThreadLocalStorage::FreeTLS(TLSKey key) { - int ret = pthread_key_delete(key); - DCHECK_EQ(ret, 0); +bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { + DCHECK(!initialized_); + int error = pthread_key_create(&key_, destructor); + if (error) { + NOTREACHED(); + return false; + } + + initialized_ = true; + return true; } -void* PlatformThreadLocalStorage::GetTLSValue(TLSKey key) { - return pthread_getspecific(key); +void ThreadLocalStorage::StaticSlot::Free() { + DCHECK(initialized_); + int error = pthread_key_delete(key_); + if (error) + NOTREACHED(); + initialized_ = false; } -void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) { - int ret = pthread_setspecific(key, value); - DCHECK_EQ(ret, 0); +void* ThreadLocalStorage::StaticSlot::Get() const { + DCHECK(initialized_); + return pthread_getspecific(key_); } -} // namespace internal +void ThreadLocalStorage::StaticSlot::Set(void* value) { + DCHECK(initialized_); + int error = pthread_setspecific(key_, value); + if (error) + NOTREACHED(); +} } // namespace base diff --git a/base/threading/thread_local_storage_win.cc b/base/threading/thread_local_storage_win.cc index 42a7d01..0ae3cb4 100644 --- a/base/threading/thread_local_storage_win.cc +++ b/base/threading/thread_local_storage_win.cc @@ -8,34 +8,200 @@ #include "base/logging.h" -namespace base { -namespace internal { +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); -bool PlatformThreadLocalStorage::AllocTLS(TLSKey* key) { - TLSKey value = TlsAlloc(); - if (value != TLS_OUT_OF_INDEXES) { - *key = value; - return true; + // 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); + } } - return false; + 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; } -void PlatformThreadLocalStorage::FreeTLS(TLSKey key) { - BOOL ret = TlsFree(key); - DCHECK(ret); +// 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); } -void* PlatformThreadLocalStorage::GetTLSValue(TLSKey key) { - return TlsGetValue(key); +bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { + if (g_native_tls_key == TLS_OUT_OF_INDEXES || !TlsGetValue(g_native_tls_key)) + ConstructTlsVector(); + + // Grab a new slot. + slot_ = InterlockedIncrement(&g_last_used_tls_key); + DCHECK_GT(slot_, 0); + if (slot_ >= kThreadLocalStorageSize) { + NOTREACHED(); + return false; + } + + // Setup our destructor. + g_tls_destructors[slot_] = destructor; + initialized_ = true; + return true; } -void PlatformThreadLocalStorage::SetTLSValue(TLSKey key, void* value) { - BOOL ret = TlsSetValue(key, value); - DCHECK(ret); +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; } -} // namespace internal +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 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; +} } // namespace base @@ -67,7 +233,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) - base::internal::PlatformThreadLocalStorage::OnThreadExit(); + WinThreadExit(); } // .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are |