Disable the use of direct TLS in ThreadLocalDetail under MSVC
[folly.git] / folly / detail / ThreadLocalDetail.h
index 7eeb1f581780a5eb48632004428540e9b2c8a423..68676a23433e4d3d93c3f6f1722d08dcd8f1c27f 100644 (file)
@@ -14,8 +14,7 @@
  * 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>
 
@@ -41,7 +43,7 @@
 //
 // 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
@@ -80,28 +82,38 @@ struct ElementWrapper {
 
   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();
     }
   }
 
@@ -140,7 +152,7 @@ struct ThreadEntry {
 
 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
@@ -160,10 +172,14 @@ class PthreadKeyUnregister {
   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) {
@@ -177,7 +193,7 @@ class PthreadKeyUnregister {
    * 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_);
@@ -271,6 +287,12 @@ struct StaticMetaBase {
 
   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_;
@@ -288,21 +310,11 @@ struct StaticMetaBase {
 // StaticMeta; you can specify multiple Tag types to break that lock.
 template <class Tag>
 struct StaticMeta : StaticMetaBase {
-  StaticMeta() : StaticMetaBase(&StaticMeta::getThreadEntry) {
-#if FOLLY_HAVE_PTHREAD_ATFORK
-    int ret = pthread_atfork(
+  StaticMeta() : StaticMetaBase(&StaticMeta::getThreadEntrySlow) {
+    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() {
@@ -312,13 +324,31 @@ struct StaticMeta : StaticMetaBase {
     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");
@@ -326,15 +356,15 @@ struct StaticMeta : StaticMetaBase {
     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
   }
 
@@ -358,5 +388,3 @@ struct StaticMeta : StaticMetaBase {
 
 }  // namespace threadlocal_detail
 }  // namespace folly
-
-#endif /* FOLLY_DETAIL_THREADLOCALDETAIL_H_ */