Commit fe4fcdc9 authored by Orvid King's avatar Orvid King Committed by Facebook Github Bot

Re-write the pthread thread-local storage portability layer to be backed by Boost

Summary:
Boost handles ensuring destruction order is correct far better than the implementation previously being used.
This gets the Windows build working again, as it's currently hitting a thread-local static destruction order issue. Specifically, the thread_local containing the backing map for the portability implementation was being destroyed before a thread_local in rcu, which caused attempts to access thread local values that had already been freed.

Reviewed By: yfeldblum

Differential Revision: D7312978

fbshipit-source-id: 4d7f1f4d8bb0b92e0c52366078ee7ee34c4248f2
parent 829d362d
...@@ -17,71 +17,52 @@ ...@@ -17,71 +17,52 @@
#include <folly/portability/PThread.h> #include <folly/portability/PThread.h>
#if !FOLLY_HAVE_PTHREAD && _WIN32 #if !FOLLY_HAVE_PTHREAD && _WIN32
#include <unordered_map> #include <boost/thread/tss.hpp> // @manual
#include <utility>
namespace folly { namespace folly {
namespace portability { namespace portability {
namespace pthread { namespace pthread {
static thread_local struct PThreadLocalMap {
PThreadLocalMap() = default;
~PThreadLocalMap() {
for (auto kv : keyMap) {
// Call destruction callbacks if they exist.
if (kv.second.second != nullptr) {
kv.second.second(kv.second.first);
}
}
}
int createKey(pthread_key_t* key, void (*destructor)(void*)) {
auto ret = TlsAlloc();
if (ret == TLS_OUT_OF_INDEXES) {
return -1;
}
*key = ret;
keyMap.emplace(*key, std::make_pair(nullptr, destructor));
return 0;
}
int deleteKey(pthread_key_t key) {
if (!TlsFree(key)) {
return -1;
}
keyMap.erase(key);
return 0;
}
void* getKey(pthread_key_t key) {
return TlsGetValue(key);
}
int setKey(pthread_key_t key, void* value) { int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)) {
if (!TlsSetValue(key, value)) { try {
return -1; auto newKey = new boost::thread_specific_ptr<void>(destructor);
} *key = newKey;
keyMap[key].first = value;
return 0; return 0;
} catch (boost::thread_resource_error) {
return -1;
} }
std::unordered_map<pthread_key_t, std::pair<void*, void (*)(void*)>> keyMap{};
} s_tls_key_map;
int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)) {
return s_tls_key_map.createKey(key, destructor);
} }
int pthread_key_delete(pthread_key_t key) { int pthread_key_delete(pthread_key_t key) {
return s_tls_key_map.deleteKey(key); try {
auto realKey = reinterpret_cast<boost::thread_specific_ptr<void>*>(key);
delete realKey;
return 0;
} catch (boost::thread_resource_error) {
return -1;
}
} }
void* pthread_getspecific(pthread_key_t key) { void* pthread_getspecific(pthread_key_t key) {
return s_tls_key_map.getKey(key); auto realKey = reinterpret_cast<boost::thread_specific_ptr<void>*>(key);
// This can't throw as-per the documentation.
return realKey->get();
} }
int pthread_setspecific(pthread_key_t key, const void* value) { int pthread_setspecific(pthread_key_t key, const void* value) {
// Yes, the PThread API really is this bad -_-... try {
return s_tls_key_map.setKey(key, const_cast<void*>(value)); auto realKey = reinterpret_cast<boost::thread_specific_ptr<void>*>(key);
// We can't just call reset here because that would invoke the cleanup
// function, which we don't want to do.
boost::detail::set_tss_data(
realKey,
boost::shared_ptr<boost::detail::tss_cleanup_function>(),
const_cast<void*>(value),
false);
return 0;
} catch (boost::thread_resource_error) {
return -1;
}
} }
} }
} }
......
...@@ -30,7 +30,9 @@ ...@@ -30,7 +30,9 @@
namespace folly { namespace folly {
namespace portability { namespace portability {
namespace pthread { namespace pthread {
using pthread_key_t = DWORD; // In reality, this is boost::thread_specific_ptr*, but we're attempting
// to avoid introducing boost into a portability header.
using pthread_key_t = void*;
int pthread_key_create(pthread_key_t* key, void (*destructor)(void*)); int pthread_key_create(pthread_key_t* key, void (*destructor)(void*));
int pthread_key_delete(pthread_key_t key); int pthread_key_delete(pthread_key_t key);
......
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