X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FSingleton.h;h=a4fdf2036a00ef72c7d9ca92c355fe9406b0a65d;hb=49e4875c1e7d55df5913410539d299b7aa3c62bc;hp=c9868d4d5385af3feb8b58249bca01e69dbf7313;hpb=d58181e677965b609be89b49ed7fbc4e78bf70b5;p=folly.git diff --git a/folly/Singleton.h b/folly/Singleton.h index c9868d4d..a4fdf203 100644 --- a/folly/Singleton.h +++ b/folly/Singleton.h @@ -53,9 +53,9 @@ // namespace { // struct Tag1 {}; // struct Tag2 {}; -// folly::Singleton s_default(); -// folly::Singleton s1(); -// folly::Singleton s2(); +// folly::Singleton s_default; +// folly::Singleton s1; +// folly::Singleton s2; // } // ... // MyExpensiveService* svc_default = s_default.get(); @@ -71,6 +71,25 @@ // Where create and destroy are functions, Singleton::CreateFunc // Singleton::TeardownFunc. // +// 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 @@ -89,21 +108,31 @@ #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 @@ -117,8 +146,9 @@ namespace folly { // // A vault goes through a few stages of life: // -// 1. Registration phase; singletons can be registered, but no -// singleton can be created. +// 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. @@ -188,10 +218,12 @@ class TypeDescriptorHasher { // SingletonHolders. class SingletonHolderBase { public: - virtual ~SingletonHolderBase() {} + virtual ~SingletonHolderBase() = default; virtual TypeDescriptor type() = 0; virtual bool hasLiveInstance() = 0; + virtual void createInstance() = 0; + virtual bool creationStarted() = 0; virtual void destroyInstance() = 0; protected: @@ -212,18 +244,20 @@ struct SingletonHolder : public SingletonHolderBase { inline T* get(); inline std::weak_ptr get_weak(); + inline std::shared_ptr try_get(); + inline folly::ReadMostlySharedPtr try_get_fast(); void registerSingleton(CreateFunc c, TeardownFunc t); void registerSingletonMock(CreateFunc c, TeardownFunc t); - virtual TypeDescriptor type(); - virtual bool hasLiveInstance(); - virtual void destroyInstance(); + virtual TypeDescriptor type() override; + virtual bool hasLiveInstance() override; + virtual void createInstance() override; + virtual bool creationStarted() override; + virtual void destroyInstance() override; private: SingletonHolder(TypeDescriptor type, SingletonVault& vault); - void createInstance(); - enum class SingletonHolderState { NotRegistered, Dead, @@ -241,18 +275,20 @@ struct SingletonHolder : public SingletonHolderBase { std::atomic state_{SingletonHolderState::NotRegistered}; // the thread creating the singleton (only valid while creating an object) - std::thread::id creating_thread_; + std::atomic creating_thread_; // The singleton itself and related functions. - // holds a shared_ptr to singleton instance, set when state is changed from - // Dead to Living. Reset when state is changed from Living to Dead. - std::shared_ptr instance_; + // 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_; // weak_ptr to the singleton instance, set when state is changed from Dead // to Living. We never write to this object after initialization, so it is // safe to read it from different threads w/o synchronization if we know // that state is set to Living std::weak_ptr instance_weak_; + // Fast equivalent of instance_weak_ + folly::ReadMostlyWeakPtr instance_weak_fast_; // Time we wait on destroy_baton after releasing Singleton shared_ptr. std::shared_ptr> destroy_baton_; T* instance_ptr_ = nullptr; @@ -271,7 +307,10 @@ struct SingletonHolder : public SingletonHolderBase { class SingletonVault { public: - enum class Type { Strict, Relaxed }; + enum class Type { + Strict, // Singletons can't be created before registrationComplete() + Relaxed, // Singletons can be created before registrationComplete() + }; explicit SingletonVault(Type type = Type::Relaxed) : type_(type) {} @@ -285,45 +324,46 @@ class SingletonVault { // 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) { - RWSpinLock::ReadHolder rh(&stateMutex_); - - stateCheck(SingletonVaultState::Running); - - if (UNLIKELY(registrationComplete_)) { - throw std::logic_error( - "Registering singleton after registrationComplete()."); - } - - RWSpinLock::ReadHolder rhMutex(&mutex_); - CHECK_THROW(singletons_.find(entry->type()) == singletons_.end(), - std::logic_error); + void registerSingleton(detail::SingletonHolderBase* entry); - RWSpinLock::UpgradedHolder wh(&mutex_); - singletons_[entry->type()] = 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); // Mark registration is complete; no more singletons can be // registered at this point. - void registrationComplete() { - RequestContext::getStaticContext(); - std::atexit([](){ SingletonVault::singleton()->destroyInstances(); }); - - RWSpinLock::WriteHolder wh(&stateMutex_); + void registrationComplete(); - stateCheck(SingletonVaultState::Running); - - if (type_ == Type::Strict) { - for (const auto& p: singletons_) { - if (p.second->hasLiveInstance()) { - throw std::runtime_error( - "Singleton created before registration was complete."); - } - } - } + /** + * 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(); - registrationComplete_ = true; - } + /** + * 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: + * + * wangle::IOThreadPoolExecutor executor(max_concurrency_level); + * folly::Baton<> done; + * doEagerInitVia(executor, &done); + * done.wait(); // or 'timed_wait', or spin with 'try_wait' + * + */ + 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. @@ -339,6 +379,12 @@ class SingletonVault { return singletons_.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 { RWSpinLock::ReadHolder rh(&mutex_); @@ -375,7 +421,7 @@ class SingletonVault { private: template - friend class detail::SingletonHolder; + friend struct detail::SingletonHolder; // The two stages of life for a vault, as mentioned in the class comment. enum class SingletonVaultState { @@ -412,6 +458,7 @@ class SingletonVault { mutable folly::RWSpinLock mutex_; SingletonMap singletons_; + std::unordered_set eagerInitSingletons_; std::vector creation_order_; SingletonVaultState state_{SingletonVaultState::Running}; bool registrationComplete_{false}; @@ -423,7 +470,7 @@ class SingletonVault { // It 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::get(). +// Singleton::try_get(). template @@ -435,7 +482,7 @@ class Singleton { // Generally your program life cycle should be fine with calling // get() repeatedly rather than saving the reference, and then not // call get() during process shutdown. - static T* get() { + static T* get() __attribute__ ((__deprecated__("Replaced by try_get"))) { return getEntry().get(); } @@ -443,22 +490,33 @@ class Singleton { // singleton, you can try to do so with a weak_ptr. Avoid this when // possible but the inability to lock the weak pointer can be a // signal that the vault has been destroyed. - static std::weak_ptr get_weak() { + static std::weak_ptr + get_weak() __attribute__ ((__deprecated__("Replaced by try_get"))) { return getEntry().get_weak(); } - // Allow the Singleton instance to also retrieve the underlying - // singleton, if desired. - T& operator*() { return *get(); } - T* operator->() { return get(); } + // Preferred alternative to get_weak, it returns shared_ptr that can be + // stored; a singleton won't be destroyed unless shared_ptr is destroyed. + // Avoid holding these shared_ptrs beyond the scope of a function; + // don't put them in member variables, always use try_get() instead + // + // try_get() can return nullptr if the singleton was destroyed, caller is + // responsible for handling nullptr return + static std::shared_ptr try_get() { + return getEntry().try_get(); + } + + static folly::ReadMostlySharedPtr try_get_fast() { + return getEntry().try_get_fast(); + } explicit Singleton(std::nullptr_t _ = nullptr, - Singleton::TeardownFunc t = nullptr) : + typename Singleton::TeardownFunc t = nullptr) : Singleton ([]() { return new T; }, std::move(t)) { } - explicit Singleton(Singleton::CreateFunc c, - Singleton::TeardownFunc t = nullptr) { + explicit Singleton(typename Singleton::CreateFunc c, + typename Singleton::TeardownFunc t = nullptr) { if (c == nullptr) { throw std::logic_error( "nullptr_t should be passed if you want T to be default constructed"); @@ -469,6 +527,28 @@ class Singleton { vault->registerSingleton(&getEntry()); } + /** + * 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: + * Singleton 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; + } + /** * Construct and inject a mock singleton which should be used only from tests. * Unlike regular singletons which are initialized once per process lifetime,