diff options
author | jar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-15 22:44:18 +0000 |
---|---|---|
committer | jar@chromium.org <jar@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-15 22:44:18 +0000 |
commit | e5d5b0e8f45962bbb5cba1a6dd9a3b31eadb9429 (patch) | |
tree | a9cb51b2a64075e23961863e6277fc50106d7713 | |
parent | b5b9e9e5d16c6c0e9903e35bceebd6865b0e9b29 (diff) | |
download | chromium_src-e5d5b0e8f45962bbb5cba1a6dd9a3b31eadb9429.zip chromium_src-e5d5b0e8f45962bbb5cba1a6dd9a3b31eadb9429.tar.gz chromium_src-e5d5b0e8f45962bbb5cba1a6dd9a3b31eadb9429.tar.bz2 |
Cleanup thread_local_storage on Windows
Remove statics that are windows only from the
header file, and relocated them the windows only file.
FIx up a few names to make things a bit more readable.
r=rvargas
Review URL: https://chromiumcodereview.appspot.com/8890003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@142511 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/threading/thread_local_storage.h | 13 | ||||
-rw-r--r-- | base/threading/thread_local_storage_win.cc | 187 |
2 files changed, 97 insertions, 103 deletions
diff --git a/base/threading/thread_local_storage.h b/base/threading/thread_local_storage.h index 30cbd47..ca422b9 100644 --- a/base/threading/thread_local_storage.h +++ b/base/threading/thread_local_storage.h @@ -86,19 +86,6 @@ class BASE_EXPORT ThreadLocalStorage { 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(); - - static long tls_key_; - static long tls_max_; -#endif // OS_WIN - DISALLOW_COPY_AND_ASSIGN(ThreadLocalStorage); }; diff --git a/base/threading/thread_local_storage_win.cc b/base/threading/thread_local_storage_win.cc index db66248..0ae3cb4 100644 --- a/base/threading/thread_local_storage_win.cc +++ b/base/threading/thread_local_storage_win.cc @@ -9,9 +9,26 @@ #include "base/logging.h" -namespace base { - 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; @@ -28,43 +45,26 @@ const int kMaxDestructorIterations = kThreadLocalStorageSize; // 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 ThreadLocalStorage::TLSDestructorFunc +volatile base::ThreadLocalStorage::TLSDestructorFunc g_tls_destructors[kThreadLocalStorageSize]; -} // namespace anonymous - -// 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; - -void** ThreadLocalStorage::Initialize() { - if (tls_key_ == TLS_OUT_OF_INDEXES) { +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 (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. + 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(tls_key_)); + 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 @@ -77,72 +77,22 @@ void** ThreadLocalStorage::Initialize() { 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(tls_key_, stack_allocated_tls_data); + 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(tls_key_, tls_data); + TlsSetValue(g_native_tls_key, tls_data); return tls_data; } -ThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) { - initialized_ = false; - slot_ = 0; - Initialize(destructor); -} - -bool ThreadLocalStorage::StaticSlot::Initialize(TLSDestructorFunc destructor) { - if (tls_key_ == TLS_OUT_OF_INDEXES || !TlsGetValue(tls_key_)) - ThreadLocalStorage::Initialize(); - - // Grab a new slot. - slot_ = InterlockedIncrement(&tls_max_) - 1; - DCHECK_GT(slot_, 0); - if (slot_ >= kThreadLocalStorageSize) { - NOTREACHED(); - return false; - } - - // 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**>(TlsGetValue(tls_key_)); - if (!tls_data) - tls_data = ThreadLocalStorage::Initialize(); - 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(tls_key_)); - if (!tls_data) - tls_data = ThreadLocalStorage::Initialize(); - DCHECK_GT(slot_, 0); - DCHECK_LT(slot_, kThreadLocalStorageSize); - tls_data[slot_] = value; -} - -void ThreadLocalStorage::ThreadExit() { - if (tls_key_ == TLS_OUT_OF_INDEXES) +// 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(tls_key_)); + void** tls_data = static_cast<void**>(TlsGetValue(g_native_tls_key)); // Maybe we have never initialized TLS for this thread. if (!tls_data) return; @@ -159,7 +109,7 @@ void ThreadLocalStorage::ThreadExit() { 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(tls_key_, stack_allocated_tls_data); + TlsSetValue(g_native_tls_key, stack_allocated_tls_data); delete[] tls_data; // Our last dependence on an allocator. int remaining_attempts = kMaxDestructorIterations; @@ -172,11 +122,12 @@ void ThreadLocalStorage::ThreadExit() { // 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 = tls_max_ - 1; slot > 0; --slot) { + for (int slot = g_last_used_tls_key; slot > 0; --slot) { void* value = stack_allocated_tls_data[slot]; if (value == NULL) continue; - TLSDestructorFunc destructor = g_tls_destructors[slot]; + base::ThreadLocalStorage::TLSDestructorFunc destructor = + g_tls_destructors[slot]; if (destructor == NULL) continue; stack_allocated_tls_data[slot] = NULL; // pre-clear the slot. @@ -193,7 +144,63 @@ void ThreadLocalStorage::ThreadExit() { } // Remove our stack allocated vector. - TlsSetValue(tls_key_, NULL); + 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(); + + // 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 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**>(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 @@ -226,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::ThreadLocalStorage::ThreadExit(); + WinThreadExit(); } // .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are |