forked from facebook/rocksdb
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Summary: This is not a generic thread local implementation in the sense that it only takes pointer. But it does support multiple instances per thread and lets user plugin function to perform cleanup when thread exits or an instance gets destroyed. Test Plan: unit test for now Reviewers: haobo, igor, sdong, dhruba Reviewed By: igor CC: leveldb, kailiu Differential Revision: https://reviews.facebook.net/D16131
- Loading branch information
Lei Jin
committed
Feb 26, 2014
1 parent
4209516
commit b2795b7
Showing
8 changed files
with
878 additions
and
6 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,236 @@ | ||
// 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" | ||
|
||
#if defined(__GNUC__) && __GNUC__ >= 4 | ||
#define UNLIKELY(x) (__builtin_expect((x), 0)) | ||
#else | ||
#define UNLIKELY(x) (x) | ||
#endif | ||
|
||
namespace rocksdb { | ||
|
||
std::unique_ptr<ThreadLocalPtr::StaticMeta> ThreadLocalPtr::StaticMeta::inst_; | ||
port::Mutex ThreadLocalPtr::StaticMeta::mutex_; | ||
#if !defined(OS_MACOSX) | ||
__thread ThreadLocalPtr::ThreadData* ThreadLocalPtr::StaticMeta::tls_ = nullptr; | ||
#endif | ||
|
||
ThreadLocalPtr::StaticMeta* ThreadLocalPtr::StaticMeta::Instance() { | ||
if (UNLIKELY(inst_ == nullptr)) { | ||
MutexLock l(&mutex_); | ||
if (inst_ == nullptr) { | ||
inst_.reset(new StaticMeta()); | ||
} | ||
} | ||
return inst_.get(); | ||
} | ||
|
||
void ThreadLocalPtr::StaticMeta::OnThreadExit(void* ptr) { | ||
auto* tls = static_cast<ThreadData*>(ptr); | ||
assert(tls != nullptr); | ||
|
||
auto* inst = Instance(); | ||
pthread_setspecific(inst->pthread_key_, nullptr); | ||
|
||
MutexLock l(&mutex_); | ||
inst->RemoveThreadData(tls); | ||
// Unref stored pointers of current thread from all instances | ||
uint32_t id = 0; | ||
for (auto& e : tls->entries) { | ||
void* raw = e.ptr.load(std::memory_order_relaxed); | ||
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) { | ||
throw std::runtime_error("pthread_key_create failed"); | ||
} | ||
head_.next = &head_; | ||
head_.prev = &head_; | ||
} | ||
|
||
void ThreadLocalPtr::StaticMeta::AddThreadData(ThreadLocalPtr::ThreadData* d) { | ||
mutex_.AssertHeld(); | ||
d->next = &head_; | ||
d->prev = head_.prev; | ||
head_.prev->next = d; | ||
head_.prev = d; | ||
} | ||
|
||
void ThreadLocalPtr::StaticMeta::RemoveThreadData( | ||
ThreadLocalPtr::ThreadData* d) { | ||
mutex_.AssertHeld(); | ||
d->next->prev = d->prev; | ||
d->prev->next = d->next; | ||
d->next = d->prev = d; | ||
} | ||
|
||
ThreadLocalPtr::ThreadData* ThreadLocalPtr::StaticMeta::GetThreadLocal() { | ||
#if defined(OS_MACOSX) | ||
// Make this local variable name look like a member variable so that we | ||
// can share all the code below | ||
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 | ||
MutexLock l(&mutex_); | ||
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) { | ||
{ | ||
MutexLock l(&mutex_); | ||
inst->RemoveThreadData(tls_); | ||
} | ||
delete tls_; | ||
throw std::runtime_error("pthread_setspecific failed"); | ||
} | ||
} | ||
return tls_; | ||
} | ||
|
||
void* ThreadLocalPtr::StaticMeta::Get(uint32_t id) const { | ||
auto* tls = GetThreadLocal(); | ||
if (UNLIKELY(id >= tls->entries.size())) { | ||
return nullptr; | ||
} | ||
return tls->entries[id].ptr.load(std::memory_order_relaxed); | ||
} | ||
|
||
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 | ||
MutexLock l(&mutex_); | ||
tls->entries.resize(id + 1); | ||
} | ||
tls->entries[id].ptr.store(ptr, std::memory_order_relaxed); | ||
} | ||
|
||
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 | ||
MutexLock l(&mutex_); | ||
tls->entries.resize(id + 1); | ||
} | ||
return tls->entries[id].ptr.exchange(ptr, std::memory_order_relaxed); | ||
} | ||
|
||
void ThreadLocalPtr::StaticMeta::Scrape(uint32_t id, autovector<void*>* ptrs) { | ||
MutexLock l(&mutex_); | ||
for (ThreadData* t = head_.next; t != &head_; t = t->next) { | ||
if (id < t->entries.size()) { | ||
void* ptr = | ||
t->entries[id].ptr.exchange(nullptr, std::memory_order_relaxed); | ||
if (ptr != nullptr) { | ||
ptrs->push_back(ptr); | ||
} | ||
} | ||
} | ||
} | ||
|
||
void ThreadLocalPtr::StaticMeta::SetHandler(uint32_t id, UnrefHandler handler) { | ||
MutexLock l(&mutex_); | ||
handler_map_[id] = handler; | ||
} | ||
|
||
UnrefHandler ThreadLocalPtr::StaticMeta::GetHandler(uint32_t id) { | ||
mutex_.AssertHeld(); | ||
auto iter = handler_map_.find(id); | ||
if (iter == handler_map_.end()) { | ||
return nullptr; | ||
} | ||
return iter->second; | ||
} | ||
|
||
uint32_t ThreadLocalPtr::StaticMeta::GetId() { | ||
MutexLock l(&mutex_); | ||
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 { | ||
MutexLock l(&mutex_); | ||
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 | ||
MutexLock l(&mutex_); | ||
auto unref = GetHandler(id); | ||
for (ThreadData* t = head_.next; t != &head_; t = t->next) { | ||
if (id < t->entries.size()) { | ||
void* ptr = | ||
t->entries[id].ptr.exchange(nullptr, std::memory_order_relaxed); | ||
if (ptr != nullptr && unref != nullptr) { | ||
unref(ptr); | ||
} | ||
} | ||
} | ||
handler_map_[id] = nullptr; | ||
free_instance_ids_.push_back(id); | ||
} | ||
|
||
ThreadLocalPtr::ThreadLocalPtr(UnrefHandler handler) | ||
: id_(StaticMeta::Instance()->GetId()) { | ||
if (handler != nullptr) { | ||
StaticMeta::Instance()->SetHandler(id_, handler); | ||
} | ||
} | ||
|
||
ThreadLocalPtr::~ThreadLocalPtr() { | ||
StaticMeta::Instance()->ReclaimId(id_); | ||
} | ||
|
||
void* ThreadLocalPtr::Get() const { | ||
return StaticMeta::Instance()->Get(id_); | ||
} | ||
|
||
void ThreadLocalPtr::Reset(void* ptr) { | ||
StaticMeta::Instance()->Reset(id_, ptr); | ||
} | ||
|
||
void* ThreadLocalPtr::Swap(void* ptr) { | ||
return StaticMeta::Instance()->Swap(id_, ptr); | ||
} | ||
|
||
void ThreadLocalPtr::Scrape(autovector<void*>* ptrs) { | ||
StaticMeta::Instance()->Scrape(id_, ptrs); | ||
} | ||
|
||
} // namespace rocksdb |
Oops, something went wrong.