Commit e64700bd authored by Dan Melnic's avatar Dan Melnic Committed by Facebook Github Bot

Delay threadEntry destruction until all instances have completed StaticMetaBase::onThreadExit

Summary: Delay threadEntry destruction until all instances have completed StaticMetaBase::onThreadExit

Reviewed By: djwatson

Differential Revision: D7471153

fbshipit-source-id: 709a54ed2d616c5dc906b923e9fdd862ac87de9c
parent 580c1197
...@@ -28,23 +28,46 @@ StaticMetaBase::StaticMetaBase(ThreadEntry* (*threadEntry)(), bool strict) ...@@ -28,23 +28,46 @@ StaticMetaBase::StaticMetaBase(ThreadEntry* (*threadEntry)(), bool strict)
PthreadKeyUnregister::registerKey(pthreadKey_); PthreadKeyUnregister::registerKey(pthreadKey_);
} }
void StaticMetaBase::onThreadExit(void* ptr) { ThreadEntryList* StaticMetaBase::getThreadEntryList() {
#ifdef FOLLY_TLD_USE_FOLLY_TLS #ifdef FOLLY_TLD_USE_FOLLY_TLS
auto threadEntry = static_cast<ThreadEntry*>(ptr); static FOLLY_TLS ThreadEntryList threadEntryListSingleton;
return &threadEntryListSingleton;
#else #else
std::unique_ptr<ThreadEntry> threadEntry(static_cast<ThreadEntry*>(ptr)); static bool init = false;
static std::mutex lock;
static pthread_key_t pthreadKey;
{
std::lock_guard<std::mutex> guard(lock);
if (!init) {
int ret = pthread_key_create(&pthreadKey, nullptr);
checkPosixError(ret, "pthread_key_create failed");
PthreadKeyUnregister::registerKey(pthreadKey);
}
}
ThreadEntryList* threadEntryList =
static_cast<ThreadEntryList*>(pthread_getspecific(pthreadKey));
if (UNLIKELY(!threadEntryList)) {
threadEntryList = new ThreadEntryList();
int ret = pthread_setspecific(pthreadKey, threadEntryList);
checkPosixError(ret, "pthread_setspecific failed");
}
return threadEntryList;
#endif #endif
DCHECK_GT(threadEntry->elementsCapacity, 0u); }
auto& meta = *threadEntry->meta;
// Make sure this ThreadEntry is available if ThreadLocal A is accessed in void StaticMetaBase::onThreadExit(void* ptr) {
// ThreadLocal B destructor. auto threadEntry = static_cast<ThreadEntry*>(ptr);
pthread_setspecific(meta.pthreadKey_, &(*threadEntry));
SCOPE_EXIT {
pthread_setspecific(meta.pthreadKey_, nullptr);
};
{ {
auto& meta = *threadEntry->meta;
// Make sure this ThreadEntry is available if ThreadLocal A is accessed in
// ThreadLocal B destructor.
pthread_setspecific(meta.pthreadKey_, threadEntry);
SharedMutex::ReadHolder rlock(nullptr); SharedMutex::ReadHolder rlock(nullptr);
if (meta.strict_) { if (meta.strict_) {
rlock = SharedMutex::ReadHolder(meta.accessAllThreadsLock_); rlock = SharedMutex::ReadHolder(meta.accessAllThreadsLock_);
...@@ -67,10 +90,63 @@ void StaticMetaBase::onThreadExit(void* ptr) { ...@@ -67,10 +90,63 @@ void StaticMetaBase::onThreadExit(void* ptr) {
} }
} }
} }
pthread_setspecific(meta.pthreadKey_, nullptr);
}
auto threadEntryList = threadEntry->list;
DCHECK_GT(threadEntryList->count, 0u);
--threadEntryList->count;
if (threadEntryList->count) {
return;
}
// dispose all the elements
for (bool shouldRunOuter = true; shouldRunOuter;) {
shouldRunOuter = false;
auto tmp = threadEntryList->head;
while (tmp) {
auto& meta = *tmp->meta;
pthread_setspecific(meta.pthreadKey_, tmp);
SharedMutex::ReadHolder rlock(nullptr);
if (meta.strict_) {
rlock = SharedMutex::ReadHolder(meta.accessAllThreadsLock_);
}
for (bool shouldRunInner = true; shouldRunInner;) {
shouldRunInner = false;
FOR_EACH_RANGE (i, 0, tmp->elementsCapacity) {
if (tmp->elements[i].dispose(TLPDestructionMode::THIS_THREAD)) {
shouldRunInner = true;
shouldRunOuter = true;
}
}
}
pthread_setspecific(meta.pthreadKey_, nullptr);
tmp = tmp->listNext;
}
} }
free(threadEntry->elements);
threadEntry->elements = nullptr; // free the entry list
threadEntry->meta = nullptr; auto head = threadEntryList->head;
threadEntryList->head = nullptr;
while (head) {
auto tmp = head;
head = head->listNext;
if (tmp->elements) {
free(tmp->elements);
tmp->elements = nullptr;
tmp->elementsCapacity = 0;
}
#ifndef FOLLY_TLD_USE_FOLLY_TLS
delete tmp;
#endif
}
#ifndef FOLLY_TLD_USE_FOLLY_TLS
delete threadEntryList;
#endif
} }
uint32_t StaticMetaBase::allocate(EntryID* ent) { uint32_t StaticMetaBase::allocate(EntryID* ent) {
......
...@@ -141,6 +141,7 @@ struct ElementWrapper { ...@@ -141,6 +141,7 @@ struct ElementWrapper {
}; };
struct StaticMetaBase; struct StaticMetaBase;
struct ThreadEntryList;
/** /**
* Per-thread entry. Each thread using a StaticMeta object has one. * Per-thread entry. Each thread using a StaticMeta object has one.
...@@ -153,9 +154,16 @@ struct ThreadEntry { ...@@ -153,9 +154,16 @@ struct ThreadEntry {
size_t elementsCapacity{0}; size_t elementsCapacity{0};
ThreadEntry* next{nullptr}; ThreadEntry* next{nullptr};
ThreadEntry* prev{nullptr}; ThreadEntry* prev{nullptr};
ThreadEntryList* list{nullptr};
ThreadEntry* listNext{nullptr};
StaticMetaBase* meta{nullptr}; StaticMetaBase* meta{nullptr};
}; };
struct ThreadEntryList {
ThreadEntry* head{nullptr};
size_t count{0};
};
constexpr uint32_t kEntryIDInvalid = std::numeric_limits<uint32_t>::max(); constexpr uint32_t kEntryIDInvalid = std::numeric_limits<uint32_t>::max();
struct PthreadKeyUnregisterTester; struct PthreadKeyUnregisterTester;
...@@ -279,6 +287,8 @@ struct StaticMetaBase { ...@@ -279,6 +287,8 @@ struct StaticMetaBase {
t->next = t->prev = t; t->next = t->prev = t;
} }
static ThreadEntryList* getThreadEntryList();
static void onThreadExit(void* ptr); static void onThreadExit(void* ptr);
uint32_t allocate(EntryID* ent); uint32_t allocate(EntryID* ent);
...@@ -370,12 +380,18 @@ struct StaticMeta : StaticMetaBase { ...@@ -370,12 +380,18 @@ struct StaticMeta : StaticMetaBase {
ThreadEntry* threadEntry = ThreadEntry* threadEntry =
static_cast<ThreadEntry*>(pthread_getspecific(key)); static_cast<ThreadEntry*>(pthread_getspecific(key));
if (!threadEntry) { if (!threadEntry) {
ThreadEntryList* threadEntryList = StaticMeta::getThreadEntryList();
#ifdef FOLLY_TLD_USE_FOLLY_TLS #ifdef FOLLY_TLD_USE_FOLLY_TLS
static FOLLY_TLS ThreadEntry threadEntrySingleton; static FOLLY_TLS ThreadEntry threadEntrySingleton;
threadEntry = &threadEntrySingleton; threadEntry = &threadEntrySingleton;
#else #else
threadEntry = new ThreadEntry(); threadEntry = new ThreadEntry();
#endif #endif
threadEntry->list = threadEntryList;
threadEntry->listNext = threadEntryList->head;
threadEntryList->head = threadEntry;
threadEntryList->count++;
threadEntry->meta = &meta; threadEntry->meta = &meta;
int ret = pthread_setspecific(key, threadEntry); int ret = pthread_setspecific(key, threadEntry);
checkPosixError(ret, "pthread_setspecific failed"); checkPosixError(ret, "pthread_setspecific failed");
......
...@@ -115,3 +115,21 @@ TEST(SingletonThreadLocalTest, AccessAfterFastPathDestruction) { ...@@ -115,3 +115,21 @@ TEST(SingletonThreadLocalTest, AccessAfterFastPathDestruction) {
th.join(); th.join();
EXPECT_EQ(6, counter); EXPECT_EQ(6, counter);
} }
TEST(ThreadLocal, TagDependencyTest) {
struct mytag {};
typedef SingletonThreadLocal<int> SingletonInt;
struct barstruct {
~barstruct() {
SingletonInt::get()++;
}
};
typedef SingletonThreadLocal<barstruct, mytag> BarSingleton;
std::thread([&]() {
SingletonInt::get();
BarSingleton::get();
})
.join();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment