Fix folly::Singleton to work in dynamically linked binaries
authorAndrii Grynenko <andrii@fb.com>
Fri, 19 Feb 2016 19:02:35 +0000 (11:02 -0800)
committerfacebook-github-bot-0 <folly-bot@fb.com>
Fri, 19 Feb 2016 19:20:31 +0000 (11:20 -0800)
Summary:This implements StaticSingletonManager which is then used to create all leaked Meyers singletons.

StaticSingletonManager is a singleton itself, which is created in a separate compilation unit (Singleton.cpp) and so we can be sure that other compilation units will always see a single instance of StaticSingletonManager, even if linked dynamically.

StaticSingletonManager then keeps a dictionary of typeid -> object pointer, which is used to de-duplicate same singleton being re-created from different compilation units (linked dynamically), usually because of code inlining.

override-unit-failures

Reviewed By: yfeldblum

Differential Revision: D2913027

fb-gh-sync-id: 1f5015a79a7a8297ebf5f0fe3fd0cc7eb44f706b
shipit-source-id: 1f5015a79a7a8297ebf5f0fe3fd0cc7eb44f706b

folly/Singleton-inl.h
folly/Singleton.cpp
folly/Singleton.h

index 340d5796c16f97c908e77c7317e66c7f46208029..83ef3926a6dfe1c9e26d05251d94ef26e45f3750 100644 (file)
@@ -21,9 +21,11 @@ namespace detail {
 template <typename T>
 template <typename Tag, typename VaultTag>
 SingletonHolder<T>& SingletonHolder<T>::singleton() {
 template <typename T>
 template <typename Tag, typename VaultTag>
 SingletonHolder<T>& SingletonHolder<T>::singleton() {
-  static auto entry = new SingletonHolder<T>(
-    {typeid(T), typeid(Tag)},
-    *SingletonVault::singleton<VaultTag>());
+  static auto entry =
+      createGlobal<SingletonHolder<T>, std::pair<Tag, VaultTag>>([]() {
+        return new SingletonHolder<T>({typeid(T), typeid(Tag)},
+                                      *SingletonVault::singleton<VaultTag>());
+      });
   return *entry;
 }
 
   return *entry;
 }
 
index 8e9cd99b0763138bd8eb7c61819da5c293a61703..cf1ce727fe31295e47d6f965e87a510126082b34 100644 (file)
@@ -25,6 +25,12 @@ namespace folly {
 
 namespace detail {
 
 
 namespace detail {
 
+// This implementation should always live in .cpp file.
+StaticSingletonManager& StaticSingletonManager::instance() {
+  static StaticSingletonManager* instance = new StaticSingletonManager();
+  return *instance;
+}
+
 constexpr std::chrono::seconds SingletonHolderBase::kDestroyWaitTime;
 
 }
 constexpr std::chrono::seconds SingletonHolderBase::kDestroyWaitTime;
 
 }
index bbff0ef250818799ba07989488fe5889c99ac0a9..e083e35b6d5dae5ed6c908d576d6999a018514a7 100644 (file)
@@ -160,6 +160,47 @@ class SingletonVault;
 
 namespace detail {
 
 
 namespace detail {
 
+// This internal-use-only class is used to create all leaked Meyers singletons.
+// It guarantees that only one instance of every such singleton will ever be
+// created, even when requested from different compilation units linked
+// dynamically.
+class StaticSingletonManager {
+ public:
+  static StaticSingletonManager& instance();
+
+  template <typename T, typename Tag, typename F>
+  inline T* create(F&& creator) {
+    std::lock_guard<std::mutex> lg(mutex_);
+
+    auto& id = typeid(TypePair<T, Tag>);
+    auto& ptr = reinterpret_cast<T*&>(map_[id]);
+    if (!ptr) {
+      ptr = creator();
+    }
+    return ptr;
+  }
+
+ private:
+  template <typename A, typename B>
+  class TypePair {};
+
+  StaticSingletonManager() {}
+
+  std::unordered_map<std::type_index, intptr_t> map_;
+  std::mutex mutex_;
+};
+
+template <typename T, typename Tag, typename F>
+inline T* createGlobal(F&& creator) {
+  return StaticSingletonManager::instance().create<T, Tag>(
+      std::forward<F>(creator));
+}
+
+template <typename T, typename Tag>
+inline T* createGlobal() {
+  return createGlobal<T, Tag>([]() { return new T(); });
+}
+
 struct DefaultTag {};
 
 // A TypeDescriptor is the unique handle for a given singleton.  It is
 struct DefaultTag {};
 
 // A TypeDescriptor is the unique handle for a given singleton.  It is
@@ -428,15 +469,18 @@ class SingletonVault {
   // tests only.
   template <typename VaultTag = detail::DefaultTag>
   static SingletonVault* singleton() {
   // tests only.
   template <typename VaultTag = detail::DefaultTag>
   static SingletonVault* singleton() {
-    static SingletonVault* vault = new SingletonVault();
+    static SingletonVault* vault =
+        detail::createGlobal<SingletonVault, VaultTag>();
     return vault;
   }
 
   typedef std::string(*StackTraceGetterPtr)();
 
   static std::atomic<StackTraceGetterPtr>& stackTraceGetter() {
     return vault;
   }
 
   typedef std::string(*StackTraceGetterPtr)();
 
   static std::atomic<StackTraceGetterPtr>& stackTraceGetter() {
-    static std::atomic<StackTraceGetterPtr> stackTraceGetterPtr;
-    return stackTraceGetterPtr;
+    static std::atomic<StackTraceGetterPtr>* stackTraceGetterPtr =
+        detail::createGlobal<std::atomic<StackTraceGetterPtr>,
+                             SingletonVault>();
+    return *stackTraceGetterPtr;
   }
 
  private:
   }
 
  private:
@@ -644,7 +688,7 @@ class LeakySingleton {
   };
 
   static Entry& entryInstance() {
   };
 
   static Entry& entryInstance() {
-    static auto entry = new Entry();
+    static auto entry = detail::createGlobal<Entry, Tag>();
     return *entry;
   }
 
     return *entry;
   }