#include "util/posix_logger.h"
#include "util/string_util.h"
#include "util/sync_point.h"
+#include "util/thread_local.h"
#include "util/thread_status_updater.h"
#if !defined(TMPFS_MAGIC)
}
Env* Env::Default() {
+ // The following function call initializes the singletons of ThreadLocalPtr
+ // right before the static default_env. This guarantees default_env will
+ // always being destructed before the ThreadLocalPtr singletons get
+ // destructed as C++ guarantees that the destructions of static variables
+ // is in the reverse order of their constructions.
+ //
+ // Since static members are destructed in the reverse order
+ // of their construction, having this call here guarantees that
+ // the destructor of static PosixEnv will go first, then the
+ // the singletons of ThreadLocalPtr.
+ ThreadLocalPtr::InitSingletons();
static PosixEnv default_env;
return &default_env;
}
namespace rocksdb {
-port::Mutex ThreadLocalPtr::StaticMeta::mutex_;
#if ROCKSDB_SUPPORT_THREAD_LOCAL
__thread ThreadLocalPtr::ThreadData* ThreadLocalPtr::StaticMeta::tls_ = nullptr;
#endif
#endif // OS_WIN
+void ThreadLocalPtr::InitSingletons() {
+ ThreadLocalPtr::StaticMeta::InitSingletons();
+ ThreadLocalPtr::Instance();
+}
+
ThreadLocalPtr::StaticMeta* ThreadLocalPtr::Instance() {
+ // Here we prefer function static variable instead of global
+ // static variable as function static variable is initialized
+ // when the function is first call. As a result, we can properly
+ // control their construction order by properly preparing their
+ // first function call.
static ThreadLocalPtr::StaticMeta inst;
return &inst;
}
+void ThreadLocalPtr::StaticMeta::InitSingletons() { Mutex(); }
+
+port::Mutex* ThreadLocalPtr::StaticMeta::Mutex() {
+ // Here we prefer function static variable instead of global
+ // static variable as function static variable is initialized
+ // when the function is first call. As a result, we can properly
+ // control their construction order by properly preparing their
+ // first function call.
+ static port::Mutex mutex;
+ return &mutex;
+}
+
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_);
+ MutexLock l(Mutex());
inst->RemoveThreadData(tls);
// Unref stored pointers of current thread from all instances
uint32_t id = 0;
}
void ThreadLocalPtr::StaticMeta::AddThreadData(ThreadLocalPtr::ThreadData* d) {
- mutex_.AssertHeld();
+ Mutex()->AssertHeld();
d->next = &head_;
d->prev = head_.prev;
head_.prev->next = d;
void ThreadLocalPtr::StaticMeta::RemoveThreadData(
ThreadLocalPtr::ThreadData* d) {
- mutex_.AssertHeld();
+ Mutex()->AssertHeld();
d->next->prev = d->prev;
d->prev->next = d->next;
d->next = d->prev = d;
{
// Register it in the global chain, needs to be done before thread exit
// handler registration
- MutexLock l(&mutex_);
+ 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_);
+ MutexLock l(Mutex());
inst->RemoveThreadData(tls_);
}
delete tls_;
auto* tls = GetThreadLocal();
if (UNLIKELY(id >= tls->entries.size())) {
// Need mutex to protect entries access within ReclaimId
- MutexLock l(&mutex_);
+ MutexLock l(Mutex());
tls->entries.resize(id + 1);
}
tls->entries[id].ptr.store(ptr, std::memory_order_release);
auto* tls = GetThreadLocal();
if (UNLIKELY(id >= tls->entries.size())) {
// Need mutex to protect entries access within ReclaimId
- MutexLock l(&mutex_);
+ MutexLock l(Mutex());
tls->entries.resize(id + 1);
}
return tls->entries[id].ptr.exchange(ptr, std::memory_order_acquire);
auto* tls = GetThreadLocal();
if (UNLIKELY(id >= tls->entries.size())) {
// Need mutex to protect entries access within ReclaimId
- MutexLock l(&mutex_);
+ MutexLock l(Mutex());
tls->entries.resize(id + 1);
}
return tls->entries[id].ptr.compare_exchange_strong(
void ThreadLocalPtr::StaticMeta::Scrape(uint32_t id, autovector<void*>* ptrs,
void* const replacement) {
- MutexLock l(&mutex_);
+ MutexLock l(Mutex());
for (ThreadData* t = head_.next; t != &head_; t = t->next) {
if (id < t->entries.size()) {
void* ptr =
}
void ThreadLocalPtr::StaticMeta::SetHandler(uint32_t id, UnrefHandler handler) {
- MutexLock l(&mutex_);
+ MutexLock l(Mutex());
handler_map_[id] = handler;
}
UnrefHandler ThreadLocalPtr::StaticMeta::GetHandler(uint32_t id) {
- mutex_.AssertHeld();
+ Mutex()->AssertHeld();
auto iter = handler_map_.find(id);
if (iter == handler_map_.end()) {
return nullptr;
}
uint32_t ThreadLocalPtr::StaticMeta::GetId() {
- MutexLock l(&mutex_);
+ MutexLock l(Mutex());
if (free_instance_ids_.empty()) {
return next_instance_id_++;
}
}
uint32_t ThreadLocalPtr::StaticMeta::PeekId() const {
- MutexLock l(&mutex_);
+ MutexLock l(Mutex());
if (!free_instance_ids_.empty()) {
return free_instance_ids_.back();
}
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_);
+ MutexLock l(Mutex());
auto unref = GetHandler(id);
for (ThreadData* t = head_.next; t != &head_; t = t->next) {
if (id < t->entries.size()) {
// data for all existing threads
void Scrape(autovector<void*>* ptrs, void* const replacement);
+ // Initialize the static singletons of the ThreadLocalPtr.
+ //
+ // If this function is not called, then the singletons will be
+ // automatically initialized when they are used.
+ //
+ // Calling this function twice or after the singletons have been
+ // initialized will be no-op.
+ static void InitSingletons();
+
protected:
struct Entry {
Entry() : ptr(nullptr) {}
// Register the UnrefHandler for id
void SetHandler(uint32_t id, UnrefHandler handler);
+ // Initialize all the singletons associated with StaticMeta.
+ //
+ // If this function is not called, then the singletons will be
+ // automatically initialized when they are used.
+ //
+ // Calling this function twice or after the singletons have been
+ // initialized will be no-op.
+ static void InitSingletons();
+
private:
// Get UnrefHandler for id with acquiring mutex
// REQUIRES: mutex locked
// protect inst, next_instance_id_, free_instance_ids_, head_,
// ThreadData.entries
- static port::Mutex mutex_;
+ //
+ // Note that here we prefer function static variable instead of the usual
+ // global static variable. The reason is that c++ destruction order of
+ // static variables in the reverse order of their construction order.
+ // However, C++ does not guarantee any construction order when global
+ // static variables are defined in different files, while the function
+ // static variables are initialized when their function are first called.
+ // As a result, the construction order of the function static variables
+ // can be controlled by properly invoke their first function calls in
+ // the right order.
+ //
+ // For instance, the following function contains a function static
+ // variable. We place a dummy function call of this inside
+ // Env::Default() to ensure the construction order of the construction
+ // order.
+ static port::Mutex* Mutex();
#if ROCKSDB_SUPPORT_THREAD_LOCAL
// Thread local storage
static __thread ThreadData* tls_;