From f390a90d567a2abe56fecca39ca880a998cbe24a Mon Sep 17 00:00:00 2001 From: Andrii Grynenko Date: Fri, 19 Feb 2016 11:02:35 -0800 Subject: [PATCH] Fix folly::Singleton to work in dynamically linked binaries 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 | 8 ++++--- folly/Singleton.cpp | 6 +++++ folly/Singleton.h | 52 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 59 insertions(+), 7 deletions(-) diff --git a/folly/Singleton-inl.h b/folly/Singleton-inl.h index 340d5796..83ef3926 100644 --- a/folly/Singleton-inl.h +++ b/folly/Singleton-inl.h @@ -21,9 +21,11 @@ namespace detail { template template SingletonHolder& SingletonHolder::singleton() { - static auto entry = new SingletonHolder( - {typeid(T), typeid(Tag)}, - *SingletonVault::singleton()); + static auto entry = + createGlobal, std::pair>([]() { + return new SingletonHolder({typeid(T), typeid(Tag)}, + *SingletonVault::singleton()); + }); return *entry; } diff --git a/folly/Singleton.cpp b/folly/Singleton.cpp index 8e9cd99b..cf1ce727 100644 --- a/folly/Singleton.cpp +++ b/folly/Singleton.cpp @@ -25,6 +25,12 @@ namespace folly { 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; } diff --git a/folly/Singleton.h b/folly/Singleton.h index bbff0ef2..e083e35b 100644 --- a/folly/Singleton.h +++ b/folly/Singleton.h @@ -160,6 +160,47 @@ class SingletonVault; 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 + inline T* create(F&& creator) { + std::lock_guard lg(mutex_); + + auto& id = typeid(TypePair); + auto& ptr = reinterpret_cast(map_[id]); + if (!ptr) { + ptr = creator(); + } + return ptr; + } + + private: + template + class TypePair {}; + + StaticSingletonManager() {} + + std::unordered_map map_; + std::mutex mutex_; +}; + +template +inline T* createGlobal(F&& creator) { + return StaticSingletonManager::instance().create( + std::forward(creator)); +} + +template +inline T* createGlobal() { + return createGlobal([]() { return new T(); }); +} + struct DefaultTag {}; // A TypeDescriptor is the unique handle for a given singleton. It is @@ -428,15 +469,18 @@ class SingletonVault { // tests only. template static SingletonVault* singleton() { - static SingletonVault* vault = new SingletonVault(); + static SingletonVault* vault = + detail::createGlobal(); return vault; } typedef std::string(*StackTraceGetterPtr)(); static std::atomic& stackTraceGetter() { - static std::atomic stackTraceGetterPtr; - return stackTraceGetterPtr; + static std::atomic* stackTraceGetterPtr = + detail::createGlobal, + SingletonVault>(); + return *stackTraceGetterPtr; } private: @@ -644,7 +688,7 @@ class LeakySingleton { }; static Entry& entryInstance() { - static auto entry = new Entry(); + static auto entry = detail::createGlobal(); return *entry; } -- 2.34.1