172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved. 2c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Use of this source code is governed by a BSD-style license that can be 3c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// found in the LICENSE file. 4c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 53f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread_local_storage.h" 6c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 7c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include <windows.h> 8c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 9c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#include "base/logging.h" 10c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 113f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsennamespace base { 123f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 13c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// In order to make TLS destructors work, we need to keep function 14c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// pointers to the destructor for each TLS that we allocate. 15c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// We make this work by allocating a single OS-level TLS, which 16c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// contains an array of slots for the application to use. In 17c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// parallel, we also allocate an array of destructors, which we 18c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// keep track of and call when threads terminate. 19c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 20c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// tls_key_ is the one native TLS that we use. It stores our 21c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// table. 22c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottlong ThreadLocalStorage::tls_key_ = TLS_OUT_OF_INDEXES; 23c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 24c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// tls_max_ is the high-water-mark of allocated thread local storage. 25c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// We intentionally skip 0 so that it is not confused with an 26c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// unallocated TLS slot. 27c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottlong ThreadLocalStorage::tls_max_ = 1; 28c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 29c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// An array of destructor function pointers for the slots. If 30c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// a slot has a destructor, it will be stored in its corresponding 31c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// entry in this array. 32c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottThreadLocalStorage::TLSDestructorFunc 33c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ThreadLocalStorage::tls_destructors_[kThreadLocalStorageSize]; 34c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 35c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid** ThreadLocalStorage::Initialize() { 36c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (tls_key_ == TLS_OUT_OF_INDEXES) { 37c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott long value = TlsAlloc(); 38c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(value != TLS_OUT_OF_INDEXES); 39c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 40c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Atomically test-and-set the tls_key. If the key is TLS_OUT_OF_INDEXES, 41c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // go ahead and set it. Otherwise, do nothing, as another 42c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // thread already did our dirty work. 43c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (InterlockedCompareExchange(&tls_key_, value, TLS_OUT_OF_INDEXES) != 44c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott TLS_OUT_OF_INDEXES) { 45c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // We've been shortcut. Another thread replaced tls_key_ first so we need 46c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // to destroy our index and use the one the other thread got first. 47c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott TlsFree(value); 48c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 49c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 50dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen DCHECK(!TlsGetValue(tls_key_)); 51c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 52c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Create an array to store our data. 53c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void** tls_data = new void*[kThreadLocalStorageSize]; 54c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott memset(tls_data, 0, sizeof(void*[kThreadLocalStorageSize])); 55c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott TlsSetValue(tls_key_, tls_data); 56c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return tls_data; 57c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 58c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 59c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick ScottThreadLocalStorage::Slot::Slot(TLSDestructorFunc destructor) 6072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen : initialized_(false), 6172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen slot_(0) { 62c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott Initialize(destructor); 63c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 64c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 65c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottbool ThreadLocalStorage::Slot::Initialize(TLSDestructorFunc destructor) { 66c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (tls_key_ == TLS_OUT_OF_INDEXES || !TlsGetValue(tls_key_)) 67c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott ThreadLocalStorage::Initialize(); 68c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 69c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Grab a new slot. 70c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott slot_ = InterlockedIncrement(&tls_max_) - 1; 71c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (slot_ >= kThreadLocalStorageSize) { 72c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott NOTREACHED(); 73c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return false; 74c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 75c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 76c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Setup our destructor. 77c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott tls_destructors_[slot_] = destructor; 78c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott initialized_ = true; 79c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return true; 80c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 81c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 82c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid ThreadLocalStorage::Slot::Free() { 83c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // At this time, we don't reclaim old indices for TLS slots. 84c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // So all we need to do is wipe the destructor. 85c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott tls_destructors_[slot_] = NULL; 86c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott initialized_ = false; 87c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 88c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 89c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid* ThreadLocalStorage::Slot::Get() const { 90c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void** tls_data = static_cast<void**>(TlsGetValue(tls_key_)); 91c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!tls_data) 92c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott tls_data = ThreadLocalStorage::Initialize(); 93c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(slot_ >= 0 && slot_ < kThreadLocalStorageSize); 94c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return tls_data[slot_]; 95c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 96c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 97c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid ThreadLocalStorage::Slot::Set(void* value) { 98c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void** tls_data = static_cast<void**>(TlsGetValue(tls_key_)); 99c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!tls_data) 100c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott tls_data = ThreadLocalStorage::Initialize(); 101c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott DCHECK(slot_ >= 0 && slot_ < kThreadLocalStorageSize); 102c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott tls_data[slot_] = value; 103c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 104c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 105c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scottvoid ThreadLocalStorage::ThreadExit() { 106c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (tls_key_ == TLS_OUT_OF_INDEXES) 107c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; 108c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 109c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void** tls_data = static_cast<void**>(TlsGetValue(tls_key_)); 110c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 111c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // Maybe we have never initialized TLS for this thread. 112c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (!tls_data) 113c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott return; 114c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 115c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott for (int slot = 0; slot < tls_max_; slot++) { 116c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (tls_destructors_[slot] != NULL) { 117c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott void* value = tls_data[slot]; 118c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott tls_destructors_[slot](value); 119c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 120c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott } 121c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 122c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott delete[] tls_data; 123c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 124c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // In case there are other "onexit" handlers... 125c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott TlsSetValue(tls_key_, NULL); 126c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 127c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 1283f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen} // namespace base 1293f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen 130c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Thread Termination Callbacks. 131c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Windows doesn't support a per-thread destructor with its 132c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// TLS primitives. So, we build it manually by inserting a 133c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// function to be called on each thread's exit. 134c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// This magic is from http://www.codeproject.com/threads/tls.asp 135c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// and it works for VC++ 7.0 and later. 136c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 137c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Force a reference to _tls_used to make the linker create the TLS directory 138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// if it's not already there. (e.g. if __declspec(thread) is not used). 139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Force a reference to p_thread_callback_base to prevent whole program 140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// optimization from discarding the variable. 141c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#ifdef _WIN64 142c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 143c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#pragma comment(linker, "/INCLUDE:_tls_used") 144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#pragma comment(linker, "/INCLUDE:p_thread_callback_base") 145c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 146c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#else // _WIN64 147c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 148c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#pragma comment(linker, "/INCLUDE:__tls_used") 149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#pragma comment(linker, "/INCLUDE:_p_thread_callback_base") 150c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 151c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif // _WIN64 152c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 153c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Static callback function to call with each thread termination. 154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid NTAPI OnThreadExit(PVOID module, DWORD reason, PVOID reserved) { 155c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // On XP SP0 & SP1, the DLL_PROCESS_ATTACH is never seen. It is sent on SP2+ 156c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott // and on W2K and W2K3. So don't assume it is sent. 157c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott if (DLL_THREAD_DETACH == reason || DLL_PROCESS_DETACH == reason) 1583f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen base::ThreadLocalStorage::ThreadExit(); 159c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott} 160c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 161c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// .CRT$XLA to .CRT$XLZ is an array of PIMAGE_TLS_CALLBACK pointers that are 162c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// called automatically by the OS loader code (not the CRT) when the module is 163c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// loaded and on thread creation. They are NOT called if the module has been 164c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// loaded by a LoadLibrary() call. It must have implicitly been loaded at 165c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// process startup. 166c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// By implicitly loaded, I mean that it is directly referenced by the main EXE 167c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// or by one of its dependent DLLs. Delay-loaded DLL doesn't count as being 168c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// implicitly loaded. 169c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// 170c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// See VC\crt\src\tlssup.c for reference. 171c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch 172c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// extern "C" suppresses C++ name mangling so we know the symbol name for the 173c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// linker /INCLUDE:symbol pragma above. 174c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochextern "C" { 175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The linker must not discard p_thread_callback_base. (We force a reference 176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// to this variable with a linker /INCLUDE:symbol pragma to ensure that.) If 177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// this variable is discarded, the OnThreadExit function will never be called. 178c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#ifdef _WIN64 179c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 180c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// .CRT section is merged with .rdata on x64 so it must be constant data. 181c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#pragma const_seg(".CRT$XLB") 182c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// When defining a const variable, it must have external linkage to be sure the 183c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// linker doesn't discard it. 184c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochextern const PIMAGE_TLS_CALLBACK p_thread_callback_base; 185c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst PIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit; 186c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 187c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Reset the default section. 188c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#pragma const_seg() 189c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 190c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#else // _WIN64 191c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 192c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#pragma data_seg(".CRT$XLB") 193c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochPIMAGE_TLS_CALLBACK p_thread_callback_base = OnThreadExit; 194c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 195c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott// Reset the default section. 196c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#pragma data_seg() 197c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott 198c7f5f8508d98d5952d42ed7648c2a8f30a4da156Patrick Scott#endif // _WIN64 199c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch} // extern "C" 200