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)
PthreadKeyUnregister::registerKey(pthreadKey_);
}
void StaticMetaBase::onThreadExit(void* ptr) {
ThreadEntryList* StaticMetaBase::getThreadEntryList() {
#ifdef FOLLY_TLD_USE_FOLLY_TLS
auto threadEntry = static_cast<ThreadEntry*>(ptr);
static FOLLY_TLS ThreadEntryList threadEntryListSingleton;
return &threadEntryListSingleton;
#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
DCHECK_GT(threadEntry->elementsCapacity, 0u);
}
void StaticMetaBase::onThreadExit(void* ptr) {
auto threadEntry = static_cast<ThreadEntry*>(ptr);
{
auto& meta = *threadEntry->meta;
// Make sure this ThreadEntry is available if ThreadLocal A is accessed in
// ThreadLocal B destructor.
pthread_setspecific(meta.pthreadKey_, &(*threadEntry));
SCOPE_EXIT {
pthread_setspecific(meta.pthreadKey_, nullptr);
};
{
pthread_setspecific(meta.pthreadKey_, threadEntry);
SharedMutex::ReadHolder rlock(nullptr);
if (meta.strict_) {
rlock = SharedMutex::ReadHolder(meta.accessAllThreadsLock_);
......@@ -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;
}
}
free(threadEntry->elements);
threadEntry->elements = nullptr;
threadEntry->meta = nullptr;
}
pthread_setspecific(meta.pthreadKey_, nullptr);
tmp = tmp->listNext;
}
}
// free the entry list
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) {
......
......@@ -141,6 +141,7 @@ struct ElementWrapper {
};
struct StaticMetaBase;
struct ThreadEntryList;
/**
* Per-thread entry. Each thread using a StaticMeta object has one.
......@@ -153,9 +154,16 @@ struct ThreadEntry {
size_t elementsCapacity{0};
ThreadEntry* next{nullptr};
ThreadEntry* prev{nullptr};
ThreadEntryList* list{nullptr};
ThreadEntry* listNext{nullptr};
StaticMetaBase* meta{nullptr};
};
struct ThreadEntryList {
ThreadEntry* head{nullptr};
size_t count{0};
};
constexpr uint32_t kEntryIDInvalid = std::numeric_limits<uint32_t>::max();
struct PthreadKeyUnregisterTester;
......@@ -279,6 +287,8 @@ struct StaticMetaBase {
t->next = t->prev = t;
}
static ThreadEntryList* getThreadEntryList();
static void onThreadExit(void* ptr);
uint32_t allocate(EntryID* ent);
......@@ -370,12 +380,18 @@ struct StaticMeta : StaticMetaBase {
ThreadEntry* threadEntry =
static_cast<ThreadEntry*>(pthread_getspecific(key));
if (!threadEntry) {
ThreadEntryList* threadEntryList = StaticMeta::getThreadEntryList();
#ifdef FOLLY_TLD_USE_FOLLY_TLS
static FOLLY_TLS ThreadEntry threadEntrySingleton;
threadEntry = &threadEntrySingleton;
#else
threadEntry = new ThreadEntry();
#endif
threadEntry->list = threadEntryList;
threadEntry->listNext = threadEntryList->head;
threadEntryList->head = threadEntry;
threadEntryList->count++;
threadEntry->meta = &meta;
int ret = pthread_setspecific(key, threadEntry);
checkPosixError(ret, "pthread_setspecific failed");
......
......@@ -115,3 +115,21 @@ TEST(SingletonThreadLocalTest, AccessAfterFastPathDestruction) {
th.join();
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