diff options
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/llsingleton.h | 205 |
1 files changed, 205 insertions, 0 deletions
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 859e271e26..b127f4f529 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -446,6 +446,177 @@ public: return sData.mInitState == DELETED; } +protected: + static EInitState getInitState() + { + return sData.mInitState; + } + +private: + struct SingletonData + { + // explicitly has a default constructor so that member variables are zero initialized in BSS + // and only changed by singleton logic, not constructor running during startup + EInitState mInitState; + DERIVED_TYPE* mInstance; + }; + static SingletonData sData; +}; + + +template <typename DERIVED_TYPE> +class LLParamSingleton : public LLSingletonBase +{ +private: + + template <typename... Args> + static DERIVED_TYPE* constructSingleton(Args&&... args) + { + return new DERIVED_TYPE(std::forward<Args>(args)...); + } + + // We know of no way to instruct the compiler that every subclass + // constructor MUST be private. + // However, we can make the LLPARAMSINGLETON() macro both declare + // a private constructor and provide the required friend declaration. + // How can we ensure that every subclass uses LLPARAMSINGLETON()? + // By making that macro provide a definition for this pure virtual + // method. If you get "can't instantiate class due to missing pure + // virtual method" for this method, then add LLPARAMSINGLETON(yourclass) + // in the subclass body. + virtual void you_must_use_LLSINGLETON_macro() = 0; + +protected: + // Pass DERIVED_TYPE explicitly to LLSingletonBase's constructor because, + // until our subclass constructor completes, *this isn't yet a + // full-fledged DERIVED_TYPE. + LLParamSingleton() : LLSingletonBase(LLSingletonBase::tag<DERIVED_TYPE>()) + { + // populate base-class function pointer with the static + // deleteSingleton() function for this particular specialization + mDeleteSingleton = &deleteSingleton; + + // add this new instance to the master list + LLSingleton_manage_master<DERIVED_TYPE>().add(this); + } + +public: + + virtual ~LLParamSingleton() + { + // remove this instance from the master list + LLSingleton_manage_master<DERIVED_TYPE>().remove(this); + sData.mInstance = NULL; + sData.mInitState = DELETED; + } + + // Passes arguments to DERIVED_TYPE's constructor and sets apropriate states + template <typename... Args> + static void initParamSingleton(Args&&... args) + { + sData.mInitState = CONSTRUCTING; + sData.mInstance = constructSingleton(std::forward<Args>(args)...); + sData.mInitState = INITIALIZED; + // initialize singleton after constructing it so that it can + // reference other singletons which in turn depend on it, thus + // breaking cyclic dependencies + sData.mInstance->initSingleton(); + // pop this off stack of initializing singletons + LLSingleton_manage_master<DERIVED_TYPE>().pop_initializing(sData.mInstance); + } + + /** + * @brief Immediately delete the singleton. + * + * A subsequent call to LLProxy::getInstance() will construct a new + * instance of the class. + * + * Without an explicit call to LLSingletonBase::deleteAll(), LLSingletons + * are implicitly destroyed after main() has exited and the C++ runtime is + * cleaning up statically-constructed objects. Some classes derived from + * LLSingleton have objects that are part of a runtime system that is + * terminated before main() exits. Calling the destructor of those objects + * after the termination of their respective systems can cause crashes and + * other problems during termination of the project. Using this method to + * destroy the singleton early can prevent these crashes. + * + * An example where this is needed is for a LLSingleton that has an APR + * object as a member that makes APR calls on destruction. The APR system is + * shut down explicitly before main() exits. This causes a crash on exit. + * Using this method before the call to apr_terminate() and NOT calling + * getInstance() again will prevent the crash. + */ + static void deleteSingleton() + { + delete sData.mInstance; + sData.mInstance = NULL; + sData.mInitState = DELETED; + } + + static DERIVED_TYPE* getInstance() + { + switch (sData.mInitState) + { + case UNINITIALIZED: + logerrs("Uninitialized param singleton ", + demangle(typeid(DERIVED_TYPE).name()).c_str()); + return NULL; + + case CONSTRUCTING: + logerrs("Tried to access singleton ", + demangle(typeid(DERIVED_TYPE).name()).c_str(), + " from singleton constructor!"); + return NULL; + + case INITIALIZING: + logerrs("State not supported by ", + demangle(typeid(DERIVED_TYPE).name()).c_str(), + " since it is a parametric singleton!"); + break; + + case INITIALIZED: + break; + + case DELETED: + logerrs("Trying to access deleted param singleton ", + demangle(typeid(DERIVED_TYPE).name()).c_str()); + + break; + } + + // By this point, if DERIVED_TYPE was pushed onto the initializing + // stack, it has been popped off. So the top of that stack, if any, is + // an LLSingleton that directly depends on DERIVED_TYPE. If this call + // came from another LLSingleton, rather than from vanilla application + // code, record the dependency. + sData.mInstance->capture_dependency( + LLSingleton_manage_master<DERIVED_TYPE>().get_initializing(sData.mInstance), + sData.mInitState); + return sData.mInstance; + } + + // Reference version of getInstance() + // Preferred over getInstance() as it disallows checking for NULL + static DERIVED_TYPE& instance() + { + return *getInstance(); + } + + // Has this singleton been created yet? + // Use this to avoid accessing singletons before they can safely be constructed. + static bool instanceExists() + { + return sData.mInitState == INITIALIZED; + } + + // Has this singleton been deleted? This can be useful during shutdown + // processing to avoid "resurrecting" a singleton we thought we'd already + // cleaned up. + static bool wasDeleted() + { + return sData.mInitState == DELETED; + } + private: struct SingletonData { @@ -460,6 +631,9 @@ private: template<typename T> typename LLSingleton<T>::SingletonData LLSingleton<T>::sData; +template<typename T> +typename LLParamSingleton<T>::SingletonData LLParamSingleton<T>::sData; + /** * Use LLSINGLETON(Foo); at the start of an LLSingleton<Foo> subclass body * when you want to declare an out-of-line constructor: @@ -510,4 +684,35 @@ private: \ /* LLSINGLETON() is carefully implemented to permit exactly this */ \ LLSINGLETON(DERIVED_CLASS) {} +/** +* Use LLPARAMSINGLETON(Foo); at the start of an LLParamSingleton<Foo> subclass body +* when you want to declare an out-of-line constructor: +* +* @code +* class Foo: public LLParamSingleton<Foo> +* { +* // use this macro at start of every LLSingleton subclass +* LLPARAMSINGLETON(Foo); +* public: +* // ... +* }; +* // ... +* [inline] +* Foo::Foo() { ... } +* @endcode +* +* Unfortunately, this mechanism does not permit you to define even a simple +* (but nontrivial) constructor within the class body. Use LLPARAMSINGLETON() +* and define the constructor outside the class body. If you must define it +* in a header file, use 'inline' (unless it's a template class) to avoid +* duplicate-symbol errors at link time. +*/ +#define LLPARAMSINGLETON(DERIVED_CLASS, ...) \ +private: \ + /* implement LLSingleton pure virtual method whose sole purpose */ \ + /* is to remind people to use this macro */ \ + virtual void you_must_use_LLSINGLETON_macro() {} \ + friend class LLParamSingleton<DERIVED_CLASS>; \ + DERIVED_CLASS(__VA_ARGS__) + #endif |