* limitations under the License.
*/
-#ifndef FOLLY_DETAIL_THREADLOCALDETAIL_H_
-#define FOLLY_DETAIL_THREADLOCALDETAIL_H_
+#pragma once
#include <limits.h>
#include <pthread.h>
#include <glog/logging.h>
-#include <folly/Foreach.h>
#include <folly/Exception.h>
+#include <folly/Foreach.h>
+#include <folly/Function.h>
#include <folly/Malloc.h>
#include <folly/MicroSpinLock.h>
+#include <folly/Portability.h>
+#include <folly/ScopeGuard.h>
#include <folly/detail/StaticSingletonManager.h>
//
// XXX: Ideally we would instead determine if emutls is in use at runtime as it
// is possible to configure glibc on Linux to use emutls regardless.
-#if !__APPLE__ && !__ANDROID__
+#if !FOLLY_MOBILE && !defined(__APPLE__) && !defined(_MSC_VER)
#define FOLLY_TLD_USE_FOLLY_TLS 1
#else
#undef FOLLY_TLD_USE_FOLLY_TLS
template <class Ptr>
void set(Ptr p) {
+ auto guard = makeGuard([&] { delete p; });
DCHECK(ptr == nullptr);
DCHECK(deleter1 == nullptr);
if (p) {
ptr = p;
- deleter1 =
- +[](void* pt, TLPDestructionMode) { delete static_cast<Ptr>(pt); };
+ deleter1 = [](void* pt, TLPDestructionMode) {
+ delete static_cast<Ptr>(pt);
+ };
ownsDeleter = false;
+ guard.dismiss();
}
}
template <class Ptr, class Deleter>
- void set(Ptr p, Deleter d) {
+ void set(Ptr p, const Deleter& d) {
+ auto guard = makeGuard([&] {
+ if (p) {
+ d(p, TLPDestructionMode::THIS_THREAD);
+ }
+ });
DCHECK(ptr == nullptr);
DCHECK(deleter2 == nullptr);
if (p) {
ptr = p;
+ auto d2 = d; // gcc-4.8 doesn't decay types correctly in lambda captures
deleter2 = new std::function<DeleterFunType>(
- [d](void* pt, TLPDestructionMode mode) {
- d(static_cast<Ptr>(pt), mode);
+ [d2](void* pt, TLPDestructionMode mode) {
+ d2(static_cast<Ptr>(pt), mode);
});
ownsDeleter = true;
+ guard.dismiss();
}
}
constexpr uint32_t kEntryIDInvalid = std::numeric_limits<uint32_t>::max();
-class PthreadKeyUnregisterTester;
+struct PthreadKeyUnregisterTester;
/**
* We want to disable onThreadExit call at the end of shutdown, we don't care
static constexpr size_t kMaxKeys = 1UL << 16;
~PthreadKeyUnregister() {
+ // If static constructor priorities are not supported then
+ // ~PthreadKeyUnregister logic is not safe.
+#if !defined(__APPLE__) && !defined(_MSC_VER)
MSLGuard lg(lock_);
while (size_) {
pthread_key_delete(keys_[--size_]);
}
+#endif
}
static void registerKey(pthread_key_t key) {
* usage.
*/
constexpr PthreadKeyUnregister() : lock_(), size_(0), keys_() { }
- friend class folly::threadlocal_detail::PthreadKeyUnregisterTester;
+ friend struct folly::threadlocal_detail::PthreadKeyUnregisterTester;
void registerKeyImpl(pthread_key_t key) {
MSLGuard lg(lock_);
}
};
- explicit StaticMetaBase(ThreadEntry* (*threadEntry)());
+ StaticMetaBase(ThreadEntry* (*threadEntry)(), bool strict);
- ~StaticMetaBase() {
+ [[noreturn]] ~StaticMetaBase() {
LOG(FATAL) << "StaticMeta lives forever!";
}
ElementWrapper& get(EntryID* ent);
+ static void initAtFork();
+ static void registerAtFork(
+ folly::Function<void()> prepare,
+ folly::Function<void()> parent,
+ folly::Function<void()> child);
+
uint32_t nextId_;
std::vector<uint32_t> freeIds_;
std::mutex lock_;
+ SharedMutex accessAllThreadsLock_;
pthread_key_t pthreadKey_;
ThreadEntry head_;
ThreadEntry* (*threadEntry_)();
+ bool strict_;
};
// Held in a singleton to track our global instances.
// Creating and destroying ThreadLocalPtr objects, as well as thread exit
// for threads that use ThreadLocalPtr objects collide on a lock inside
// StaticMeta; you can specify multiple Tag types to break that lock.
-template <class Tag>
+template <class Tag, class AccessMode>
struct StaticMeta : StaticMetaBase {
- StaticMeta() : StaticMetaBase(&StaticMeta::getThreadEntry) {
-#if FOLLY_HAVE_PTHREAD_ATFORK
- int ret = pthread_atfork(
+ StaticMeta()
+ : StaticMetaBase(
+ &StaticMeta::getThreadEntrySlow,
+ std::is_same<AccessMode, AccessModeStrict>::value) {
+ registerAtFork(
/*prepare*/ &StaticMeta::preFork,
/*parent*/ &StaticMeta::onForkParent,
/*child*/ &StaticMeta::onForkChild);
- checkPosixError(ret, "pthread_atfork failed");
-#elif !__ANDROID__ && !defined(_MSC_VER)
- // pthread_atfork is not part of the Android NDK at least as of n9d. If
- // something is trying to call native fork() directly at all with Android's
- // process management model, this is probably the least of the problems.
- //
- // But otherwise, this is a problem.
- #warning pthread_atfork unavailable
-#endif
}
- static StaticMeta<Tag>& instance() {
+ static StaticMeta<Tag, AccessMode>& instance() {
// Leak it on exit, there's only one per process and we don't have to
// worry about synchronization with exiting threads.
- static auto instance = detail::createGlobal<StaticMeta<Tag>, void>();
+ /* library-local */ static auto instance =
+ detail::createGlobal<StaticMeta<Tag, AccessMode>, void>();
return *instance;
}
+ ElementWrapper& get(EntryID* ent) {
+ ThreadEntry* threadEntry = getThreadEntry();
+ uint32_t id = ent->getOrInvalid();
+ // if id is invalid, it is equal to uint32_t's max value.
+ // x <= max value is always true
+ if (UNLIKELY(threadEntry->elementsCapacity <= id)) {
+ reserve(ent);
+ id = ent->getOrInvalid();
+ assert(threadEntry->elementsCapacity > id);
+ }
+ return threadEntry->elements[id];
+ }
+
static ThreadEntry* getThreadEntrySlow() {
auto& meta = instance();
auto key = meta.pthreadKey_;
ThreadEntry* threadEntry =
static_cast<ThreadEntry*>(pthread_getspecific(key));
if (!threadEntry) {
+#ifdef FOLLY_TLD_USE_FOLLY_TLS
+ static FOLLY_TLS ThreadEntry threadEntrySingleton;
+ threadEntry = &threadEntrySingleton;
+#else
threadEntry = new ThreadEntry();
+#endif
threadEntry->meta = &meta;
int ret = pthread_setspecific(key, threadEntry);
checkPosixError(ret, "pthread_setspecific failed");
return threadEntry;
}
- static ThreadEntry* getThreadEntry() {
+ inline static ThreadEntry* getThreadEntry() {
#ifdef FOLLY_TLD_USE_FOLLY_TLS
static FOLLY_TLS ThreadEntry* threadEntryCache{nullptr};
if (UNLIKELY(threadEntryCache == nullptr)) {
- threadEntryCache = getThreadEntrySlow();
+ threadEntryCache = instance().threadEntry_();
}
return threadEntryCache;
#else
- return getThreadEntrySlow();
+ return instance().threadEntry_();
#endif
}
} // namespace threadlocal_detail
} // namespace folly
-
-#endif /* FOLLY_DETAIL_THREADLOCALDETAIL_H_ */