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