Improve RequestContext::getStaticContext() perf
authorAndrii Grynenko <andrii@fb.com>
Thu, 4 Feb 2016 21:58:25 +0000 (13:58 -0800)
committerfacebook-github-bot-0 <folly-bot@fb.com>
Thu, 4 Feb 2016 22:20:26 +0000 (14:20 -0800)
Summary: Avoid using folly::ThreadLocal on the fast-path. This uses the fact that it is a static object.

Reviewed By: yfeldblum

Differential Revision: D2873374

fb-gh-sync-id: 978108da0c9e8576fda6849058b0262478c2841e

folly/Makefile.am
folly/Singleton.cpp
folly/Singleton.h
folly/SingletonThreadLocal.h [new file with mode: 0644]
folly/io/async/Request.h

index d989ac3628b61149fba5582ce0e98088cd2c8120..a88acae83575bee8ad9a10c5eb0efe90b99fc323 100644 (file)
@@ -274,6 +274,7 @@ nobase_follyinclude_HEADERS = \
        SharedMutex.h \
        Singleton.h \
        Singleton-inl.h \
+       SingletonThreadLocal.h \
        SmallLocks.h \
        small_vector.h \
        SocketAddress.h \
index c2a8afdef314714f4fd4d33236048291503e2447..66321bfdc1e47c4e9a37285b405d8b0bb39cef51 100644 (file)
@@ -96,7 +96,6 @@ void SingletonVault::addEagerInitSingleton(detail::SingletonHolderBase* entry) {
 }
 
 void SingletonVault::registrationComplete() {
-  RequestContext::saveContext();
   std::atexit([](){ SingletonVault::singleton()->destroyInstances(); });
 
   RWSpinLock::WriteHolder wh(&stateMutex_);
@@ -214,8 +213,6 @@ void SingletonVault::reenableInstances() {
 }
 
 void SingletonVault::scheduleDestroyInstances() {
-  RequestContext::saveContext();
-
   class SingletonVaultDestructor {
    public:
     ~SingletonVaultDestructor() {
index 174f3ca4fed3401508bee89077f890852c8fddcd..732a6112d9df8ae690aac8cfb33fb20674d6afa9 100644 (file)
 #include <folly/RWSpinLock.h>
 #include <folly/Demangle.h>
 #include <folly/Executor.h>
-#include <folly/io/async/Request.h>
 #include <folly/experimental/ReadMostlySharedPtr.h>
 
 #include <algorithm>
@@ -609,6 +608,75 @@ class Singleton {
   }
 };
 
+template <typename T, typename Tag = detail::DefaultTag>
+class LeakySingleton {
+ public:
+  using CreateFunc = std::function<T*()>;
+
+  LeakySingleton() : LeakySingleton([] { return new T(); }) {}
+
+  explicit LeakySingleton(CreateFunc createFunc) {
+    auto& entry = entryInstance();
+    if (entry.state != State::NotRegistered) {
+      LOG(FATAL) << "Double registration of singletons of the same "
+                 << "underlying type; check for multiple definitions "
+                 << "of type folly::LeakySingleton<" + entry.type_.name() + ">";
+    }
+    entry.createFunc = createFunc;
+    entry.state = State::Dead;
+  }
+
+  static T& get() { return instance(); }
+
+ private:
+  enum class State { NotRegistered, Dead, Living };
+
+  struct Entry {
+    Entry() {}
+    Entry(const Entry&) = delete;
+    Entry& operator=(const Entry&) = delete;
+
+    std::atomic<State> state{State::NotRegistered};
+    T* ptr{nullptr};
+    CreateFunc createFunc;
+    std::mutex mutex;
+    detail::TypeDescriptor type_{typeid(T), typeid(Tag)};
+  };
+
+  static Entry& entryInstance() {
+    static auto entry = new Entry();
+    return *entry;
+  }
+
+  static T& instance() {
+    auto& entry = entryInstance();
+    if (UNLIKELY(entry.state != State::Living)) {
+      createInstance();
+    }
+
+    return *entry.ptr;
+  }
+
+  static void createInstance() {
+    auto& entry = entryInstance();
+
+    std::lock_guard<std::mutex> lg(entry.mutex);
+    if (entry.state == State::Living) {
+      return;
+    }
+
+    if (entry.state == State::NotRegistered) {
+      auto ptr = SingletonVault::stackTraceGetter().load();
+      LOG(FATAL) << "Creating instance for unregistered singleton: "
+                 << entry.type_.name() << "\n"
+                 << "Stacktrace:"
+                 << "\n" << (ptr ? (*ptr)() : "(not available)");
+    }
+
+    entry.ptr = entry.createFunc();
+    entry.state = State::Living;
+  }
+};
 }
 
 #include <folly/Singleton-inl.h>
diff --git a/folly/SingletonThreadLocal.h b/folly/SingletonThreadLocal.h
new file mode 100644 (file)
index 0000000..e073fec
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2016 Facebook, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#pragma once
+
+#include <folly/ThreadLocal.h>
+#include <folly/Singleton.h>
+
+namespace folly {
+
+template <typename T, typename Tag = detail::DefaultTag>
+class SingletonThreadLocal {
+ public:
+  using CreateFunc = std::function<T*(void)>;
+
+  SingletonThreadLocal() : SingletonThreadLocal([]() { return new T(); }) {}
+
+  explicit SingletonThreadLocal(CreateFunc createFunc)
+      : singleton_([createFunc = std::move(createFunc)]() mutable {
+          return new ThreadLocalT([createFunc =
+                                       std::move(createFunc)]() mutable {
+            return new Wrapper(std::unique_ptr<T>(createFunc()));
+          });
+        }) {}
+
+  static T& get() {
+#ifdef FOLLY_TLS
+    *localPtr() = nullptr;
+    if (UNLIKELY(*localPtr() == nullptr)) {
+      *localPtr() = &(**SingletonT::get());
+    }
+
+    return **localPtr();
+#else
+    return **SingletonT::get();
+#endif
+  }
+
+ private:
+#ifdef FOLLY_TLS
+  static T** localPtr() {
+    static FOLLY_TLS T* localPtr = nullptr;
+    return &localPtr;
+  }
+#endif
+
+  class Wrapper {
+   public:
+    explicit Wrapper(std::unique_ptr<T> t) : t_(std::move(t)) {}
+
+    ~Wrapper() {
+#ifdef FOLLY_TLS
+      *localPtr() = nullptr;
+#endif
+    }
+
+    T& operator*() { return *t_; }
+
+   private:
+    std::unique_ptr<T> t_;
+  };
+
+  using ThreadLocalT = ThreadLocal<Wrapper>;
+  using SingletonT = LeakySingleton<ThreadLocalT, Tag>;
+
+  SingletonT singleton_;
+};
+}
index 24245e5ca98938b156f5daae5aba9bcd2a347b9c..24361d484997966dcfeceb1110fae714b1da6bce 100644 (file)
@@ -25,6 +25,7 @@
 #include <glog/logging.h>
 #include <folly/ThreadLocal.h>
 #include <folly/RWSpinLock.h>
+#include <folly/SingletonThreadLocal.h>
 
 namespace folly {
 
@@ -129,15 +130,10 @@ class RequestContext {
   }
 
  private:
-  // Used to solve static destruction ordering issue.  Any static object
-  // that uses RequestContext must call this function in its constructor.
-  //
-  // See below link for more details.
-  // http://stackoverflow.com/questions/335369/
-  // finding-c-static-initialization-order-problems#335746
-  static std::shared_ptr<RequestContext> &getStaticContext() {
-    static folly::ThreadLocal<std::shared_ptr<RequestContext> > context;
-    return *context;
+  static std::shared_ptr<RequestContext>& getStaticContext() {
+    using SingletonT = SingletonThreadLocal<std::shared_ptr<RequestContext>>;
+    static SingletonT singleton;
+    return singleton.get();
   }
 
   folly::RWSpinLock lock;