thread_local.cc 10.5 KB
Newer Older
L
Lei Jin 已提交
1 2 3 4 5 6 7 8 9 10 11
//  Copyright (c) 2013, Facebook, Inc.  All rights reserved.
//  This source code is licensed under the BSD-style license found in the
//  LICENSE file in the root directory of this source tree. An additional grant
//  of patent rights can be found in the PATENTS file in the same directory.
//
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#include "util/thread_local.h"
#include "util/mutexlock.h"
12
#include "port/likely.h"
A
Anders Bakken 已提交
13
#include <stdlib.h>
14

L
Lei Jin 已提交
15 16
namespace rocksdb {

17
port::Mutex ThreadLocalPtr::StaticMeta::mutex_;
D
Dmitri Smirnov 已提交
18
#if !defined(OS_MACOSX) && !defined(OS_WIN)
L
Lei Jin 已提交
19 20 21
__thread ThreadLocalPtr::ThreadData* ThreadLocalPtr::StaticMeta::tls_ = nullptr;
#endif

D
Dmitri Smirnov 已提交
22 23 24 25 26 27
// Windows doesn't support a per-thread destructor with its
// TLS primitives.  So, we build it manually by inserting a
// function to be called on each thread's exit.
// See http://www.codeproject.com/Articles/8113/Thread-Local-Storage-The-C-Way
// and http://www.nynaeve.net/?p=183
//
S
sdong 已提交
28 29 30 31
// really we do this to have clear conscience since using TLS with thread-pools
// is iffy
// although OK within a request. But otherwise, threads have no identity in its
// modern use.
D
Dmitri Smirnov 已提交
32 33 34 35 36

// This runs on windows only called from the System Loader
#ifdef OS_WIN

// Windows cleanup routine is invoked from a System Loader with a different
S
sdong 已提交
37 38 39 40
// signature so we can not directly hookup the original OnThreadExit which is
// private member
// so we make StaticMeta class share with the us the address of the function so
// we can invoke it.
D
Dmitri Smirnov 已提交
41 42 43
namespace wintlscleanup {

// This is set to OnThreadExit in StaticMeta singleton constructor
S
sdong 已提交
44
UnrefHandler thread_local_inclass_routine = nullptr;
D
Dmitri Smirnov 已提交
45 46 47 48
pthread_key_t thread_local_key = -1;

// Static callback function to call with each thread termination.
void NTAPI WinOnThreadExit(PVOID module, DWORD reason, PVOID reserved) {
49 50 51 52
  // We decided to punt on PROCESS_EXIT
  if (DLL_THREAD_DETACH == reason) {
    if (thread_local_key != -1 && thread_local_inclass_routine != nullptr) {
      void* tls = pthread_getspecific(thread_local_key);
S
sdong 已提交
53
      if (tls != nullptr) {
54 55 56 57
        thread_local_inclass_routine(tls);
      }
    }
  }
D
Dmitri Smirnov 已提交
58 59
}

S
sdong 已提交
60
}  // wintlscleanup
D
Dmitri Smirnov 已提交
61

S
sdong 已提交
62
#ifdef _WIN64
D
Dmitri Smirnov 已提交
63

S
sdong 已提交
64 65
#pragma comment(linker, "/include:_tls_used")
#pragma comment(linker, "/include:p_thread_callback_on_exit")
D
Dmitri Smirnov 已提交
66 67 68

#else  // _WIN64

S
sdong 已提交
69 70
#pragma comment(linker, "/INCLUDE:__tls_used")
#pragma comment(linker, "/INCLUDE:_p_thread_callback_on_exit")
D
Dmitri Smirnov 已提交
71

S
sdong 已提交
72
#endif  // _WIN64
D
Dmitri Smirnov 已提交
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87

// extern "C" suppresses C++ name mangling so we know the symbol name for the
// linker /INCLUDE:symbol pragma above.
extern "C" {

// The linker must not discard thread_callback_on_exit.  (We force a reference
// to this variable with a linker /include:symbol pragma to ensure that.) If
// this variable is discarded, the OnThreadExit function will never be called.
#ifdef _WIN64

// .CRT section is merged with .rdata on x64 so it must be constant data.
#pragma const_seg(".CRT$XLB")
// When defining a const variable, it must have external linkage to be sure the
// linker doesn't discard it.
extern const PIMAGE_TLS_CALLBACK p_thread_callback_on_exit;
S
sdong 已提交
88 89
const PIMAGE_TLS_CALLBACK p_thread_callback_on_exit =
    wintlscleanup::WinOnThreadExit;
D
Dmitri Smirnov 已提交
90 91 92 93 94 95 96 97 98 99 100 101 102 103
// Reset the default section.
#pragma const_seg()

#else  // _WIN64

#pragma data_seg(".CRT$XLB")
PIMAGE_TLS_CALLBACK p_thread_callback_on_exit = wintlscleanup::WinOnThreadExit;
// Reset the default section.
#pragma data_seg()

#endif  // _WIN64

}  // extern "C"

S
sdong 已提交
104
#endif  // OS_WIN
D
Dmitri Smirnov 已提交
105

L
Lei Jin 已提交
106 107 108
ThreadLocalPtr::StaticMeta* ThreadLocalPtr::Instance() {
  static ThreadLocalPtr::StaticMeta inst;
  return &inst;
L
Lei Jin 已提交
109 110 111 112 113 114 115 116 117
}

void ThreadLocalPtr::StaticMeta::OnThreadExit(void* ptr) {
  auto* tls = static_cast<ThreadData*>(ptr);
  assert(tls != nullptr);

  auto* inst = Instance();
  pthread_setspecific(inst->pthread_key_, nullptr);

118
  MutexLock l(&mutex_);
L
Lei Jin 已提交
119 120 121 122
  inst->RemoveThreadData(tls);
  // Unref stored pointers of current thread from all instances
  uint32_t id = 0;
  for (auto& e : tls->entries) {
123
    void* raw = e.ptr.load();
L
Lei Jin 已提交
124 125 126 127 128 129 130 131 132 133 134 135 136 137
    if (raw != nullptr) {
      auto unref = inst->GetHandler(id);
      if (unref != nullptr) {
        unref(raw);
      }
    }
    ++id;
  }
  // Delete thread local structure no matter if it is Mac platform
  delete tls;
}

ThreadLocalPtr::StaticMeta::StaticMeta() : next_instance_id_(0) {
  if (pthread_key_create(&pthread_key_, &OnThreadExit) != 0) {
138
    abort();
L
Lei Jin 已提交
139
  }
A
Alexey Maykov 已提交
140 141 142

  // OnThreadExit is not getting called on the main thread.
  // Call through the static destructor mechanism to avoid memory leak.
N
Nate Rosenblum 已提交
143 144 145 146 147 148 149 150 151 152 153
  //
  // Caveats: ~A() will be invoked _after_ ~StaticMeta for the global
  // singleton (destructors are invoked in reverse order of constructor
  // _completion_); the latter must not mutate internal members. This
  // cleanup mechanism inherently relies on use-after-release of the
  // StaticMeta, and is brittle with respect to compiler-specific handling
  // of memory backing destructed statically-scoped objects. Perhaps
  // registering with atexit(3) would be more robust.
  //
  // This is not required on Windows.
#if !defined(OS_WIN)
A
Alexey Maykov 已提交
154 155
  static struct A {
    ~A() {
N
Nate Rosenblum 已提交
156 157 158 159
#if defined(OS_MACOSX)
      ThreadData* tls_ =
        static_cast<ThreadData*>(pthread_getspecific(Instance()->pthread_key_));
#endif
A
Alexey Maykov 已提交
160 161 162 163 164
      if (tls_) {
        OnThreadExit(tls_);
      }
    }
  } a;
N
Nate Rosenblum 已提交
165
#endif
A
Alexey Maykov 已提交
166

L
Lei Jin 已提交
167 168
  head_.next = &head_;
  head_.prev = &head_;
D
Dmitri Smirnov 已提交
169 170

#ifdef OS_WIN
S
sdong 已提交
171
  // Share with Windows its cleanup routine and the key
D
Dmitri Smirnov 已提交
172 173 174
  wintlscleanup::thread_local_inclass_routine = OnThreadExit;
  wintlscleanup::thread_local_key = pthread_key_;
#endif
L
Lei Jin 已提交
175 176 177
}

void ThreadLocalPtr::StaticMeta::AddThreadData(ThreadLocalPtr::ThreadData* d) {
178
  mutex_.AssertHeld();
L
Lei Jin 已提交
179 180 181 182 183 184 185 186
  d->next = &head_;
  d->prev = head_.prev;
  head_.prev->next = d;
  head_.prev = d;
}

void ThreadLocalPtr::StaticMeta::RemoveThreadData(
    ThreadLocalPtr::ThreadData* d) {
187
  mutex_.AssertHeld();
L
Lei Jin 已提交
188 189 190 191 192 193
  d->next->prev = d->prev;
  d->prev->next = d->next;
  d->next = d->prev = d;
}

ThreadLocalPtr::ThreadData* ThreadLocalPtr::StaticMeta::GetThreadLocal() {
D
Dmitri Smirnov 已提交
194
#if defined(OS_MACOSX) || defined(OS_WIN)
S
sdong 已提交
195 196
  // Make this local variable name look like a member variable so that we
  // can share all the code below
L
Lei Jin 已提交
197 198 199 200 201 202 203 204 205 206
  ThreadData* tls_ =
      static_cast<ThreadData*>(pthread_getspecific(Instance()->pthread_key_));
#endif

  if (UNLIKELY(tls_ == nullptr)) {
    auto* inst = Instance();
    tls_ = new ThreadData();
    {
      // Register it in the global chain, needs to be done before thread exit
      // handler registration
207
      MutexLock l(&mutex_);
L
Lei Jin 已提交
208 209 210 211 212 213
      inst->AddThreadData(tls_);
    }
    // Even it is not OS_MACOSX, need to register value for pthread_key_ so that
    // its exit handler will be triggered.
    if (pthread_setspecific(inst->pthread_key_, tls_) != 0) {
      {
214
        MutexLock l(&mutex_);
L
Lei Jin 已提交
215 216 217
        inst->RemoveThreadData(tls_);
      }
      delete tls_;
218
      abort();
L
Lei Jin 已提交
219 220 221 222 223 224 225 226 227 228
    }
  }
  return tls_;
}

void* ThreadLocalPtr::StaticMeta::Get(uint32_t id) const {
  auto* tls = GetThreadLocal();
  if (UNLIKELY(id >= tls->entries.size())) {
    return nullptr;
  }
229
  return tls->entries[id].ptr.load(std::memory_order_acquire);
L
Lei Jin 已提交
230 231 232 233 234 235
}

void ThreadLocalPtr::StaticMeta::Reset(uint32_t id, void* ptr) {
  auto* tls = GetThreadLocal();
  if (UNLIKELY(id >= tls->entries.size())) {
    // Need mutex to protect entries access within ReclaimId
236
    MutexLock l(&mutex_);
L
Lei Jin 已提交
237 238
    tls->entries.resize(id + 1);
  }
239
  tls->entries[id].ptr.store(ptr, std::memory_order_release);
L
Lei Jin 已提交
240 241 242 243 244 245
}

void* ThreadLocalPtr::StaticMeta::Swap(uint32_t id, void* ptr) {
  auto* tls = GetThreadLocal();
  if (UNLIKELY(id >= tls->entries.size())) {
    // Need mutex to protect entries access within ReclaimId
246
    MutexLock l(&mutex_);
L
Lei Jin 已提交
247 248
    tls->entries.resize(id + 1);
  }
249
  return tls->entries[id].ptr.exchange(ptr, std::memory_order_acquire);
L
Lei Jin 已提交
250 251
}

252 253 254 255 256
bool ThreadLocalPtr::StaticMeta::CompareAndSwap(uint32_t id, void* ptr,
    void*& expected) {
  auto* tls = GetThreadLocal();
  if (UNLIKELY(id >= tls->entries.size())) {
    // Need mutex to protect entries access within ReclaimId
257
    MutexLock l(&mutex_);
258 259
    tls->entries.resize(id + 1);
  }
260 261
  return tls->entries[id].ptr.compare_exchange_strong(
      expected, ptr, std::memory_order_release, std::memory_order_relaxed);
262 263 264 265
}

void ThreadLocalPtr::StaticMeta::Scrape(uint32_t id, autovector<void*>* ptrs,
    void* const replacement) {
266
  MutexLock l(&mutex_);
L
Lei Jin 已提交
267 268 269
  for (ThreadData* t = head_.next; t != &head_; t = t->next) {
    if (id < t->entries.size()) {
      void* ptr =
270
          t->entries[id].ptr.exchange(replacement, std::memory_order_acquire);
L
Lei Jin 已提交
271 272 273 274 275 276 277 278
      if (ptr != nullptr) {
        ptrs->push_back(ptr);
      }
    }
  }
}

void ThreadLocalPtr::StaticMeta::SetHandler(uint32_t id, UnrefHandler handler) {
279
  MutexLock l(&mutex_);
L
Lei Jin 已提交
280 281 282 283
  handler_map_[id] = handler;
}

UnrefHandler ThreadLocalPtr::StaticMeta::GetHandler(uint32_t id) {
284
  mutex_.AssertHeld();
L
Lei Jin 已提交
285 286 287 288 289 290 291 292
  auto iter = handler_map_.find(id);
  if (iter == handler_map_.end()) {
    return nullptr;
  }
  return iter->second;
}

uint32_t ThreadLocalPtr::StaticMeta::GetId() {
293
  MutexLock l(&mutex_);
L
Lei Jin 已提交
294 295 296 297 298 299 300 301 302 303
  if (free_instance_ids_.empty()) {
    return next_instance_id_++;
  }

  uint32_t id = free_instance_ids_.back();
  free_instance_ids_.pop_back();
  return id;
}

uint32_t ThreadLocalPtr::StaticMeta::PeekId() const {
304
  MutexLock l(&mutex_);
L
Lei Jin 已提交
305 306 307 308 309 310 311 312 313
  if (!free_instance_ids_.empty()) {
    return free_instance_ids_.back();
  }
  return next_instance_id_;
}

void ThreadLocalPtr::StaticMeta::ReclaimId(uint32_t id) {
  // This id is not used, go through all thread local data and release
  // corresponding value
314
  MutexLock l(&mutex_);
L
Lei Jin 已提交
315 316 317
  auto unref = GetHandler(id);
  for (ThreadData* t = head_.next; t != &head_; t = t->next) {
    if (id < t->entries.size()) {
318
      void* ptr = t->entries[id].ptr.exchange(nullptr);
L
Lei Jin 已提交
319 320 321 322 323 324 325 326 327 328
      if (ptr != nullptr && unref != nullptr) {
        unref(ptr);
      }
    }
  }
  handler_map_[id] = nullptr;
  free_instance_ids_.push_back(id);
}

ThreadLocalPtr::ThreadLocalPtr(UnrefHandler handler)
L
Lei Jin 已提交
329
    : id_(Instance()->GetId()) {
L
Lei Jin 已提交
330
  if (handler != nullptr) {
L
Lei Jin 已提交
331
    Instance()->SetHandler(id_, handler);
L
Lei Jin 已提交
332 333 334 335
  }
}

ThreadLocalPtr::~ThreadLocalPtr() {
L
Lei Jin 已提交
336
  Instance()->ReclaimId(id_);
L
Lei Jin 已提交
337 338 339
}

void* ThreadLocalPtr::Get() const {
L
Lei Jin 已提交
340
  return Instance()->Get(id_);
L
Lei Jin 已提交
341 342 343
}

void ThreadLocalPtr::Reset(void* ptr) {
L
Lei Jin 已提交
344
  Instance()->Reset(id_, ptr);
L
Lei Jin 已提交
345 346 347
}

void* ThreadLocalPtr::Swap(void* ptr) {
L
Lei Jin 已提交
348
  return Instance()->Swap(id_, ptr);
L
Lei Jin 已提交
349 350
}

351
bool ThreadLocalPtr::CompareAndSwap(void* ptr, void*& expected) {
L
Lei Jin 已提交
352
  return Instance()->CompareAndSwap(id_, ptr, expected);
353 354 355
}

void ThreadLocalPtr::Scrape(autovector<void*>* ptrs, void* const replacement) {
L
Lei Jin 已提交
356
  Instance()->Scrape(id_, ptrs, replacement);
L
Lei Jin 已提交
357 358 359
}

}  // namespace rocksdb