/* * Copyright (c) Meta Platforms, Inc. and affiliates. * * 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. */ // // Docs: https://fburl.com/fbcref_singleton // // SingletonVault - a library to manage the creation and destruction // of interdependent singletons. // // Recommended usage of this class: suppose you have a class // called MyExpensiveService, and you only want to construct one (ie, // it's a singleton), but you only want to construct it if it is used. // // In your .h file: // class MyExpensiveService { // // Caution - may return a null ptr during startup and shutdown. // static std::shared_ptr getInstance(); // .... // }; // // In your .cpp file: // namespace { struct PrivateTag {}; } // static folly::Singleton the_singleton; // std::shared_ptr MyExpensiveService::getInstance() { // return the_singleton.try_get(); // } // // Code in other modules can access it via: // // auto instance = MyExpensiveService::getInstance(); // // Advanced usage and notes: // // You can also access a singleton instance with // `Singleton::try_get()`. We recommend // that you prefer the form `the_singleton.try_get()` because it ensures that // `the_singleton` is used and cannot be garbage-collected during linking: this // is necessary because the constructor of `the_singleton` is what registers it // to the SingletonVault. // // The singleton will be created on demand. If the constructor for // MyExpensiveService actually makes use of *another* Singleton, then // the right thing will happen -- that other singleton will complete // construction before get() returns. However, in the event of a // circular dependency, a runtime error will occur. // // You can have multiple singletons of the same underlying type, but // each must be given a unique tag. If no tag is specified a default tag is // used. We recommend that you use a tag from an anonymous namespace private to // your implementation file, as this ensures that the singleton is only // available via your interface and not also through Singleton::try_get() // // namespace { // struct Tag1 {}; // struct Tag2 {}; // folly::Singleton s_default; // folly::Singleton s1; // folly::Singleton s2; // } // ... // MyExpensiveService* svc_default = s_default.get(); // MyExpensiveService* svc1 = s1.get(); // MyExpensiveService* svc2 = s2.get(); // // By default, the singleton instance is constructed via new and // deleted via delete, but this is configurable: // // namespace { folly::Singleton the_singleton(create, // destroy); } // // Where create and destroy are functions, Singleton::CreateFunc // Singleton::TeardownFunc. // // For example, if you need to pass arguments to your class's constructor: // class X { // public: // X(int a1, std::string a2); // // ... // } // Make your singleton like this: // folly::Singleton singleton_x([]() { return new X(42, "foo"); }); // // The above examples detail a situation where an expensive singleton is loaded // on-demand (thus only if needed). However if there is an expensive singleton // that will likely be needed, and initialization takes a potentially long time, // e.g. while initializing, parsing some files, talking to remote services, // making uses of other singletons, and so on, the initialization of those can // be scheduled up front, or "eagerly". // // In that case the singleton can be declared this way: // // namespace { // auto the_singleton = // folly::Singleton(/* optional create, destroy args */) // .shouldEagerInit(); // } // // This way the singleton's instance is built at program initialization, // if the program opted-in to that feature by calling "doEagerInit" or // "doEagerInitVia" during its startup. // // What if you need to destroy all of your singletons? Say, some of // your singletons manage threads, but you need to fork? Or your unit // test wants to clean up all global state? Then you can call // SingletonVault::singleton()->destroyInstances(), which invokes the // TeardownFunc for each singleton, in the reverse order they were // created. It is your responsibility to ensure your singletons can // handle cases where the singletons they depend on go away, however. // Singletons won't be recreated after destroyInstances call. If you // want to re-enable singleton creation (say after fork was called) you // should call reenableInstances. #pragma once #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // use this guard to handleSingleton breaking change in 3rd party code #ifndef FOLLY_SINGLETON_TRY_GET #define FOLLY_SINGLETON_TRY_GET #endif namespace folly { // For actual usage, please see the Singleton class at the bottom // of this file; that is what you will actually interact with. // SingletonVault is the class that manages singleton instances. It // is unaware of the underlying types of singletons, and simply // manages lifecycles and invokes CreateFunc and TeardownFunc when // appropriate. In general, you won't need to interact with the // SingletonVault itself. // // A vault goes through a few stages of life: // // 1. Registration phase; singletons can be registered: // a) Strict: no singleton can be created in this stage. // b) Relaxed: singleton can be created (the default vault is Relaxed). // 2. registrationComplete() has been called; singletons can no // longer be registered, but they can be created. // 3. A vault can return to stage 1 when destroyInstances is called. // // In general, you don't need to worry about any of the above; just // ensure registrationComplete() is called near the top of your main() // function, otherwise no singletons can be instantiated. class SingletonVault; namespace detail { // A TypeDescriptor is the unique handle for a given singleton. It is // a combination of the type and of the optional name, and is used as // a key in unordered_maps. class TypeDescriptor { public: TypeDescriptor(const std::type_info& ti, const std::type_info& tag_ti) : ti_(ti), tag_ti_(tag_ti) {} TypeDescriptor(const TypeDescriptor& other) : ti_(other.ti_), tag_ti_(other.tag_ti_) {} TypeDescriptor& operator=(const TypeDescriptor& other) { if (this != &other) { ti_ = other.ti_; tag_ti_ = other.tag_ti_; } return *this; } std::string name() const; friend class TypeDescriptorHasher; bool operator==(const TypeDescriptor& other) const { return ti_ == other.ti_ && tag_ti_ == other.tag_ti_; } private: std::type_index ti_; std::type_index tag_ti_; }; class TypeDescriptorHasher { public: size_t operator()(const TypeDescriptor& ti) const { return folly::hash::hash_combine(ti.ti_, ti.tag_ti_); } }; [[noreturn]] void singletonWarnLeakyDoubleRegistrationAndAbort( const TypeDescriptor& type); [[noreturn]] void singletonWarnLeakyInstantiatingNotRegisteredAndAbort( const TypeDescriptor& type); [[noreturn]] void singletonWarnRegisterMockEarlyAndAbort( const TypeDescriptor& type); void singletonWarnDestroyInstanceLeak( const TypeDescriptor& type, const void* ptr); [[noreturn]] void singletonWarnCreateCircularDependencyAndAbort( const TypeDescriptor& type); [[noreturn]] void singletonWarnCreateUnregisteredAndAbort( const TypeDescriptor& type); [[noreturn]] void singletonWarnCreateBeforeRegistrationCompleteAndAbort( const TypeDescriptor& type); void singletonPrintDestructionStackTrace(const TypeDescriptor& type); [[noreturn]] void singletonThrowNullCreator(const std::type_info& type); [[noreturn]] void singletonThrowGetInvokedAfterDestruction( const TypeDescriptor& type); struct SingletonVaultState { // The two stages of life for a vault, as mentioned in the class comment. enum class Type { Running, Quiescing, }; Type state{Type::Running}; bool registrationComplete{false}; // Each singleton in the vault can be in two states: dead // (registered but never created), living (CreateFunc returned an instance). void check( Type expected, const char* msg = "Unexpected singleton state change") const { if (expected != state) { throw_exception(msg); } } bool isDisabled() const { return state == Type::Quiescing; } }; // This interface is used by SingletonVault to interact with SingletonHolders. // Having a non-template interface allows SingletonVault to keep a list of all // SingletonHolders. class SingletonHolderBase { public: explicit SingletonHolderBase(TypeDescriptor typeDesc) noexcept : type_(typeDesc) {} virtual ~SingletonHolderBase() = default; TypeDescriptor type() const { return type_; } virtual bool hasLiveInstance() = 0; virtual void createInstance() = 0; virtual bool creationStarted() = 0; virtual void preDestroyInstance(ReadMostlyMainPtrDeleter<>&) = 0; virtual void destroyInstance() = 0; virtual void inChildAfterFork() = 0; private: TypeDescriptor type_; }; // An actual instance of a singleton, tracking the instance itself, // its state as described above, and the create and teardown // functions. template struct SingletonHolder : public SingletonHolderBase { public: typedef std::function TeardownFunc; typedef std::function CreateFunc; template inline static SingletonHolder& singleton(); inline T* get(); inline std::weak_ptr get_weak(); inline std::shared_ptr try_get(); inline folly::ReadMostlySharedPtr try_get_fast(); template inline invoke_result_t apply(Func f); inline void vivify(); void registerSingleton(CreateFunc c, TeardownFunc t); void registerSingletonMock(CreateFunc c, TeardownFunc t); bool hasLiveInstance() override; void createInstance() override; bool creationStarted() override; void preDestroyInstance(ReadMostlyMainPtrDeleter<>&) override; void destroyInstance() override; void inChildAfterFork() override; private: template struct Impl; SingletonHolder(TypeDescriptor type, SingletonVault& vault) noexcept; enum class SingletonHolderState { NotRegistered, Dead, Living, LivingInChildAfterFork, }; SingletonVault& vault_; // mutex protects the entire entry during construction/destruction std::mutex mutex_; // State of the singleton entry. If state is Living, instance_ptr and // instance_weak can be safely accessed w/o synchronization. std::atomic state_{SingletonHolderState::NotRegistered}; // the thread creating the singleton (only valid while creating an object) std::atomic creating_thread_{}; // The singleton itself and related functions. // holds a ReadMostlyMainPtr to singleton instance, set when state is changed // from Dead to Living. Reset when state is changed from Living to Dead. folly::ReadMostlyMainPtr instance_; // used to release all ReadMostlyMainPtrs at once folly::ReadMostlySharedPtr instance_copy_; // per-core shared_ptrs that in turn hold the instance shared_ptr, to avoid // contention in acquiring them. folly::CoreCachedSharedPtr instance_core_cached_; // weak references to the previous pointers. These are never written to after // initialization, so they're safe to read without synchronization once the // state has transitioned to Living. // instance_weak_ is a reference to the main instance, so it is authoritative // on whether the instance is expired. std::weak_ptr instance_weak_; folly::ReadMostlyWeakPtr instance_weak_fast_; folly::CoreCachedWeakPtr instance_weak_core_cached_; // Time we wait on destroy_baton after releasing Singleton shared_ptr. std::shared_ptr> destroy_baton_; T* instance_ptr_ = nullptr; CreateFunc create_ = nullptr; TeardownFunc teardown_ = nullptr; std::shared_ptr> print_destructor_stack_trace_; SingletonHolder(const SingletonHolder&) = delete; SingletonHolder& operator=(const SingletonHolder&) = delete; SingletonHolder& operator=(SingletonHolder&&) = delete; SingletonHolder(SingletonHolder&&) = delete; }; } // namespace detail class SingletonVault { public: enum class Type { Strict, // Singletons can't be created before registrationComplete() Relaxed, // Singletons can be created before registrationComplete() }; /** * Clears all singletons in the given vault at ctor and dtor times. * Useful for unit-tests that need to clear the world. * * This need can arise when a unit-test needs to swap out an object used by a * singleton for a test-double, but the singleton needing its dependency to be * swapped has a type or a tag local to some other translation unit and * unavailable in the current translation unit. * * Other, better approaches to this need are "plz 2 refactor" .... */ struct ScopedExpunger { SingletonVault* vault; explicit ScopedExpunger(SingletonVault* v) : vault(v) { expunge(); } ~ScopedExpunger() { expunge(); } void expunge() { vault->destroyInstances(); vault->reenableInstances(); } }; static Type defaultVaultType(); explicit SingletonVault(Type type = defaultVaultType()) noexcept; // Destructor is only called by unit tests to check destroyInstances. ~SingletonVault(); typedef std::function TeardownFunc; typedef std::function CreateFunc; // Ensure that Singleton has not been registered previously and that // registration is not complete. If validations succeeds, // register a singleton of a given type with the create and teardown // functions. void registerSingleton(detail::SingletonHolderBase* entry); /** * Called by `Singleton.shouldEagerInit()` to ensure the instance * is built when `doEagerInit[Via]` is called; see those methods * for more info. */ void addEagerInitSingleton(detail::SingletonHolderBase* entry); void addEagerInitOnReenableSingleton(detail::SingletonHolderBase* entry); // Mark registration is complete; no more singletons can be // registered at this point. void registrationComplete(); /** * Initialize all singletons which were marked as eager-initialized * (using `shouldEagerInit()`). No return value. Propagates exceptions * from constructors / create functions, as is the usual case when calling * for example `Singleton::get_weak()`. */ void doEagerInit(); /** * Schedule eager singletons' initializations through the given executor. * If baton ptr is not null, its `post` method is called after all * early initialization has completed. * * If exceptions are thrown during initialization, this method will still * `post` the baton to indicate completion. The exception will not propagate * and future attempts to `try_get` or `get_weak` the failed singleton will * retry initialization. * * Sample usage: * * folly::IOThreadPoolExecutor executor(max_concurrency_level); * folly::Baton<> done; * doEagerInitVia(executor, &done); * done.wait(); // or 'try_wait_for', etc. * */ void doEagerInitVia(Executor& exe, folly::Baton<>* done = nullptr); // Destroy all singletons; when complete, the vault can't create // singletons once again until reenableInstances() is called. // If reenableInstances() will not be called, destroyInstancesFinal() // should be used instead. void destroyInstances(); // Enable re-creating singletons after destroyInstances() was called. void reenableInstances(); // Same as destroyInstances() but reenableInstances() should not be called // after it. Starts a shutdown timer. void destroyInstancesFinal(); // For testing; how many registered and living singletons we have. size_t registeredSingletonCount() const { return singletons_.rlock()->size(); } /** * Flips to true if eager initialization was used, and has completed. * Never set to true if "doEagerInit()" or "doEagerInitVia" never called. */ bool eagerInitComplete() const; size_t livingSingletonCount() const { auto singletons = singletons_.rlock(); size_t ret = 0; for (const auto& p : *singletons) { if (p.second->hasLiveInstance()) { ++ret; } } return ret; } // A well-known vault; you can actually have others, but this is the // default. static SingletonVault* singleton() { return singleton<>(); } // Gets singleton vault for any Tag. Non-default tag should be used in unit // tests only. template static SingletonVault* singleton() { return &detail::createGlobal(); } // Get a cancellation token that gets triggered when singleton destruction // starts. // // The underlying cancellation source gets reset when reenableInstances() is // called. folly::CancellationToken getDestructionCancellationToken() { return cancellationSource_.wlock()->getToken(); } void setType(Type type) { type_.store(type, std::memory_order_relaxed); } void setShutdownTimeout(std::chrono::milliseconds shutdownTimeout) { shutdownTimeout_ = shutdownTimeout; } void disableShutdownTimeout() { shutdownTimeout_ = std::chrono::milliseconds::zero(); } void addToShutdownLog(std::string message); [[noreturn]] void fireShutdownTimer(); void setFailOnUseAfterFork(bool failOnUseAfterFork) { failOnUseAfterFork_ = failOnUseAfterFork; } bool isDisabled() const { return state_.rlock()->isDisabled(); } private: template friend struct detail::SingletonHolder; // This method only matters if registrationComplete() is never called. // Otherwise destroyInstances is scheduled to be executed atexit. // // Initializes static object, which calls destroyInstances on destruction. // Used to have better deletion ordering with singleton not managed by // folly::Singleton. The desruction will happen in the following order: // 1. Singletons, not managed by folly::Singleton, which were created after // any of the singletons managed by folly::Singleton was requested. // 2. All singletons managed by folly::Singleton // 3. Singletons, not managed by folly::Singleton, which were created before // any of the singletons managed by folly::Singleton was requested. static void scheduleDestroyInstances(); void startShutdownTimer(); typedef std::unordered_map< detail::TypeDescriptor, detail::SingletonHolderBase*, detail::TypeDescriptorHasher> SingletonMap; // Use SharedMutexSuppressTSAN to suppress noisy lock inversions when building // with TSAN. If TSAN is not enabled, SharedMutexSuppressTSAN is equivalent // to a normal SharedMutex. Synchronized singletons_; Synchronized< std::unordered_set, SharedMutexSuppressTSAN> eagerInitSingletons_; Synchronized< std::unordered_set, SharedMutexSuppressTSAN> eagerInitOnReenableSingletons_; Synchronized, SharedMutexSuppressTSAN> creationOrder_; Synchronized< std::unordered_set, SharedMutexSuppressTSAN> instantiatedAtLeastOnce_; std::unordered_set liveSingletonsPreFork_; // Using SharedMutexReadPriority is important here, because we want to make // sure we don't block nested singleton creation happening concurrently with // destroyInstances(). Synchronized state_; std::atomic type_; std::atomic shutdownTimerStarted_{false}; std::chrono::milliseconds shutdownTimeout_{std::chrono::minutes{5}}; Synchronized> shutdownLog_; // We use a lock around CancellationSource to get the guarantee that all // cancellation callbacks that got triggered on requestCancellation() are done // executing by the time we start destruction. This prevents silent callbacks // that take long to block destruction. folly::Synchronized cancellationSource_; bool failOnUseAfterFork_{true}; }; /** * Singleton allows for simple access to registering and instantiating * singletons. Create instances of this class in the global scope of * type Singleton to register your singleton for later access via * Singleton::try_get(). * * There are many supporting libraries and classes for Singleton; this is the * one that users typically interact with. */ template < typename T, typename Tag = detail::DefaultTag, typename VaultTag = detail::DefaultTag /* for testing */> class Singleton { public: typedef std::function CreateFunc; typedef std::function TeardownFunc; /** * Get a pointer to the singleton. * * It is preferable to call get() repeatedly than to store the returned * pointer. Accessing the returned pointer often fails during shutdown. * * Deprecated in favor of try_get(). */ [[deprecated("Replaced by try_get")]] static T* get() { return getEntry().get(); } /** * Get a weak_ptr to the singleton. * * If you cannot lock the weak_ptr, this usually means the vault has been * destroyed. * * Deprecated in favor of try_get(). */ [[deprecated("Replaced by try_get")]] static std::weak_ptr get_weak() { return getEntry().get_weak(); } /** * Get a shared_ptr to the singleton. * * It is recommended to call try_get() repeatedly, rather than storing the * shared_ptr, because storing the shared_ptr prevents the singleton from * being destroyed during shutdown. */ static std::shared_ptr try_get() { return getEntry().try_get(); } /** * Get a ReadMostlySharedPtr to the singleton. * * It is recommended to call try_get_fast() repeatedly, rather than storing * the ReadMostlySharedPtr, because storing the ReadMostlySharedPtr prevents * the singleton from being destroyed during shutdown. */ static folly::ReadMostlySharedPtr try_get_fast() { return getEntry().try_get_fast(); } /** * Applies a callback to the possibly-nullptr singleton instance, returning * the callback's result. * * That is, the following two are functionally equivalent: * singleton.apply(std::ref(f)); * f(singleton.try_get().get()); * * For example, the following returns the singleton * instance directly without any extra operations on the instance: * auto ret = Singleton::apply([](auto* v) { return v; }); */ template static invoke_result_t apply(Func f) { return getEntry().apply(std::ref(f)); } /// Ensure the instance exists. static void vivify() { getEntry().vivify(); } /** * Create a singleton, which uses the default-constructor for its wrapped * type. * * @param t The teardown function to use (in lieu of delete). */ explicit Singleton( std::nullptr_t /* _ */ = nullptr, typename Singleton::TeardownFunc t = nullptr) : Singleton([]() { return new T; }, std::move(t)) {} /** * @param c The create function to use (in lieu of new). * @param t The teardown function to use (in lieu of delete). */ explicit Singleton( typename Singleton::CreateFunc c, typename Singleton::TeardownFunc t = nullptr) { if (c == nullptr) { detail::singletonThrowNullCreator(typeid(T)); } auto vault = SingletonVault::singleton(); getEntry().registerSingleton(std::move(c), getTeardownFunc(std::move(t))); vault->registerSingleton(&getEntry()); } /** * Specify that the singleton should be eagerly initialized. * * Should be instantiated as soon as "doEagerInit[Via]" is called. * Singletons are usually lazy-loaded (built on-demand) but for those which * are known to be needed, to avoid the potential lag for objects that take * long to construct during runtime, there is an option to make sure these * are built up-front. * * Use like: * auto gFooInstance = Singleton(...).shouldEagerInit(); * * Or alternately, define the singleton as usual, and say * gFooInstance.shouldEagerInit(); * * at some point prior to calling registrationComplete(). * Then doEagerInit() or doEagerInitVia(Executor*) can be called. */ Singleton& shouldEagerInit() { auto vault = SingletonVault::singleton(); vault->addEagerInitSingleton(&getEntry()); return *this; } /** * Specify that the singleton should be eagerly initialized when singletons * reenable. * * Should be re-instantiated as soon as reenableInstances() is called. * Note that it will be re-instantiated only if it was instantiated before * destroyInstances() call. * * Use like: * auto gFooInstance = Singleton(...).shouldEagerInitOnReenable(); */ Singleton& shouldEagerInitOnReenable() { auto vault = SingletonVault::singleton(); vault->addEagerInitOnReenableSingleton(&getEntry()); return *this; } /** * Inject a mock singleton, for testing. * * Construct and inject a mock singleton which should be used only from tests. * Unlike regular singletons which are initialized once per process lifetime, * mock singletons live for the duration of a test. This means that one * process running multiple tests can initialize and register the same * singleton multiple times. This functionality should be used only from tests * since it relaxes validation and performance in order to be able to perform * the injection. The returned mock singleton is functionality identical to * regular singletons. */ static void make_mock( std::nullptr_t /* c */ = nullptr, typename Singleton::TeardownFunc t = nullptr) { make_mock([]() { return new T; }, t); } static void make_mock( CreateFunc c, typename Singleton::TeardownFunc t = nullptr) { if (c == nullptr) { detail::singletonThrowNullCreator(typeid(T)); } auto& entry = getEntry(); entry.registerSingletonMock(c, getTeardownFunc(t)); } private: inline static detail::SingletonHolder& getEntry() { return detail::SingletonHolder::template singleton(); } // Construct TeardownFunc. static typename detail::SingletonHolder::TeardownFunc getTeardownFunc( TeardownFunc t) { if (t == nullptr) { return [](T* v) { delete v; }; } else { return t; } } }; template class LeakySingleton { public: using CreateFunc = std::function; LeakySingleton() : LeakySingleton([] { return new T(); }) {} explicit LeakySingleton(CreateFunc createFunc) { auto& entry = entryInstance(); if (entry.state != State::NotRegistered) { detail::singletonWarnLeakyDoubleRegistrationAndAbort(entry.type_); } entry.createFunc = createFunc; entry.state = State::Dead; } static T& get() { return instance(); } static void make_mock(std::nullptr_t /* c */ = nullptr) { make_mock([]() { return new T; }); } static void make_mock(CreateFunc createFunc) { if (createFunc == nullptr) { detail::singletonThrowNullCreator(typeid(T)); } auto& entry = entryInstance(); std::lock_guard lg(entry.mutex); if (entry.ptr) { lsan_ignore_object(std::atomic_exchange(&entry.ptr, (T*)nullptr)); } entry.createFunc = createFunc; entry.state = State::Dead; } private: enum class State { NotRegistered, Dead, Living }; struct Entry { Entry() noexcept {} Entry(const Entry&) = delete; Entry& operator=(const Entry&) = delete; std::atomic state{State::NotRegistered}; std::atomic ptr{nullptr}; CreateFunc createFunc; std::mutex mutex; detail::TypeDescriptor type_{typeid(T), typeid(Tag)}; }; static Entry& entryInstance() { return detail::createGlobal(); } static T& instance() { auto& entry = entryInstance(); if (FOLLY_UNLIKELY(entry.state != State::Living)) { createInstance(); } return *entry.ptr; } static void createInstance() { auto& entry = entryInstance(); std::lock_guard lg(entry.mutex); if (entry.state == State::Living) { return; } if (entry.state == State::NotRegistered) { detail::singletonWarnLeakyInstantiatingNotRegisteredAndAbort(entry.type_); } entry.ptr = entry.createFunc(); entry.state = State::Living; } }; } // namespace folly #include