diff options
Diffstat (limited to 'indra/llcommon/llsingleton.h')
| -rw-r--r-- | indra/llcommon/llsingleton.h | 734 | 
1 files changed, 531 insertions, 203 deletions
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 859e271e26..7c81d65a8b 100644 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -27,9 +27,14 @@  #include <boost/noncopyable.hpp>  #include <boost/unordered_set.hpp> +#include <initializer_list>  #include <list> -#include <vector>  #include <typeinfo> +#include <vector> +#include "mutex.h" +#include "lockstatic.h" +#include "llthread.h"               // on_main_thread() +#include "llmainthreadtask.h"  class LLSingletonBase: private boost::noncopyable  { @@ -39,16 +44,13 @@ public:  private:      // All existing LLSingleton instances are tracked in this master list.      typedef std::list<LLSingletonBase*> list_t; -    static list_t& get_master(); -    // This, on the other hand, is a stack whose top indicates the LLSingleton -    // currently being initialized. -    static list_t& get_initializing(); -    static list_t& get_initializing_from(MasterList*); +    // Size of stack whose top indicates the LLSingleton currently being +    // initialized. +    static list_t::size_type get_initializing_size();      // Produce a vector<LLSingletonBase*> of master list, in dependency order.      typedef std::vector<LLSingletonBase*> vec_t;      static vec_t dep_sort(); -    bool mCleaned;                  // cleanupSingleton() has been called      // we directly depend on these other LLSingletons      typedef boost::unordered_set<LLSingletonBase*> set_t;      set_t mDepends; @@ -57,10 +59,11 @@ protected:      typedef enum e_init_state      {          UNINITIALIZED = 0,          // must be default-initialized state -        CONSTRUCTING, -        INITIALIZING, -        INITIALIZED, -        DELETED +        QUEUED,                     // construction queued, not yet executing +        CONSTRUCTING,               // within DERIVED_TYPE constructor +        INITIALIZING,               // within DERIVED_TYPE::initSingleton() +        INITIALIZED,                // normal case +        DELETED                     // deleteSingleton() or deleteAll() called      } EInitState;      // Define tag<T> to pass to our template constructor. You can't explicitly @@ -100,26 +103,37 @@ protected:      // That being the case, we control exactly when it happens -- and we can      // pop the stack immediately thereafter.      void pop_initializing(); -private: -    // logging -    static void log_initializing(const char* verb, const char* name); +    // Remove 'this' from the init stack in case of exception in the +    // LLSingleton subclass constructor. +    static void reset_initializing(list_t::size_type size);  protected:      // If a given call to B::getInstance() happens during either A::A() or      // A::initSingleton(), record that A directly depends on B. -    void capture_dependency(list_t& initializing, EInitState); - -    // delegate LL_ERRS() logging to llsingleton.cpp -    static void logerrs(const char* p1, const char* p2="", -                        const char* p3="", const char* p4=""); -    // delegate LL_WARNS() logging to llsingleton.cpp -    static void logwarns(const char* p1, const char* p2="", -                         const char* p3="", const char* p4=""); +    void capture_dependency(); + +    // delegate logging calls to llsingleton.cpp +public: +    typedef std::initializer_list<const std::string> string_params; +protected: +    static void logerrs  (const string_params&); +    static void logwarns (const string_params&); +    static void loginfos (const string_params&); +    static void logdebugs(const string_params&);      static std::string demangle(const char* mangled); +    // these classname() declarations restate template functions declared in +    // llerror.h because we avoid #including that here +    template <typename T> +    static std::string classname()       { return demangle(typeid(T).name()); } +    template <typename T> +    static std::string classname(T* ptr) { return demangle(typeid(*ptr).name()); }      // Default methods in case subclass doesn't declare them.      virtual void initSingleton() {}      virtual void cleanupSingleton() {} +    // internal wrapper around calls to cleanupSingleton() +    void cleanup_(); +      // deleteSingleton() isn't -- and shouldn't be -- a virtual method. It's a      // class static. However, given only Foo*, deleteAll() does need to be      // able to reach Foo::deleteSingleton(). Make LLSingleton (which declares @@ -129,32 +143,15 @@ protected:  public:      /** -     * Call this to call the cleanupSingleton() method for every LLSingleton -     * constructed since the start of the last cleanupAll() call. (Any -     * LLSingleton constructed DURING a cleanupAll() call won't be cleaned up -     * until the next cleanupAll() call.) cleanupSingleton() neither deletes -     * nor destroys its LLSingleton; therefore it's safe to include logic that -     * might take significant realtime or even throw an exception. -     * -     * The most important property of cleanupAll() is that cleanupSingleton() -     * methods are called in dependency order, leaf classes last. Thus, given -     * two LLSingleton subclasses A and B, if A's dependency on B is properly -     * expressed as a B::getInstance() or B::instance() call during either -     * A::A() or A::initSingleton(), B will be cleaned up after A. -     * -     * If a cleanupSingleton() method throws an exception, the exception is -     * logged, but cleanupAll() attempts to continue calling the rest of the -     * cleanupSingleton() methods. -     */ -    static void cleanupAll(); -    /** -     * Call this to call the deleteSingleton() method for every LLSingleton -     * constructed since the start of the last deleteAll() call. (Any -     * LLSingleton constructed DURING a deleteAll() call won't be cleaned up -     * until the next deleteAll() call.) deleteSingleton() deletes and -     * destroys its LLSingleton. Any cleanup logic that might take significant -     * realtime -- or throw an exception -- must not be placed in your -     * LLSingleton's destructor, but rather in its cleanupSingleton() method. +     * deleteAll() calls the cleanupSingleton() and deleteSingleton() methods +     * for every LLSingleton constructed since the start of the last +     * deleteAll() call. (Any LLSingleton constructed DURING a deleteAll() +     * call won't be cleaned up until the next deleteAll() call.) +     * deleteSingleton() deletes and destroys its LLSingleton. Any cleanup +     * logic that might take significant realtime -- or throw an exception -- +     * must not be placed in your LLSingleton's destructor, but rather in its +     * cleanupSingleton() method, which is called implicitly by +     * deleteSingleton().       *       * The most important property of deleteAll() is that deleteSingleton()       * methods are called in dependency order, leaf classes last. Thus, given @@ -162,9 +159,9 @@ public:       * expressed as a B::getInstance() or B::instance() call during either       * A::A() or A::initSingleton(), B will be cleaned up after A.       * -     * If a deleteSingleton() method throws an exception, the exception is -     * logged, but deleteAll() attempts to continue calling the rest of the -     * deleteSingleton() methods. +     * If a cleanupSingleton() or deleteSingleton() method throws an +     * exception, the exception is logged, but deleteAll() attempts to +     * continue calling the rest of the deleteSingleton() methods.       */      static void deleteAll();  }; @@ -178,7 +175,22 @@ struct LLSingleton_manage_master      void remove(LLSingletonBase* sb) { sb->remove_master(); }      void push_initializing(LLSingletonBase* sb) { sb->push_initializing(typeid(T).name()); }      void pop_initializing (LLSingletonBase* sb) { sb->pop_initializing(); } -    LLSingletonBase::list_t& get_initializing(T*) { return LLSingletonBase::get_initializing(); } +    // used for init stack cleanup in case an LLSingleton subclass constructor +    // throws an exception +    void reset_initializing(LLSingletonBase::list_t::size_type size) +    { +        LLSingletonBase::reset_initializing(size); +    } +    // For any LLSingleton subclass except the MasterList, obtain the size of +    // the init stack from the MasterList singleton instance. +    LLSingletonBase::list_t::size_type get_initializing_size() +    { +        return LLSingletonBase::get_initializing_size(); +    } +    void capture_dependency(LLSingletonBase* sb) +    { +        sb->capture_dependency(); +    }  };  // But for the specific case of LLSingletonBase::MasterList, don't. @@ -189,22 +201,30 @@ struct LLSingleton_manage_master<LLSingletonBase::MasterList>      void remove(LLSingletonBase*) {}      void push_initializing(LLSingletonBase*) {}      void pop_initializing (LLSingletonBase*) {} -    LLSingletonBase::list_t& get_initializing(LLSingletonBase::MasterList* instance) -    { -        return LLSingletonBase::get_initializing_from(instance); -    } +    // since we never pushed, no need to clean up +    void reset_initializing(LLSingletonBase::list_t::size_type size) {} +    LLSingletonBase::list_t::size_type get_initializing_size() { return 0; } +    void capture_dependency(LLSingletonBase*) {}  };  // Now we can implement LLSingletonBase's template constructor.  template <typename DERIVED_TYPE>  LLSingletonBase::LLSingletonBase(tag<DERIVED_TYPE>): -    mCleaned(false), -    mDeleteSingleton(NULL) +    mDeleteSingleton(nullptr)  { -    // Make this the currently-initializing LLSingleton. +    // This is the earliest possible point at which we can push this new +    // instance onto the init stack. LLSingleton::constructSingleton() can't +    // do it before calling the constructor, because it doesn't have an +    // instance pointer until the constructor returns. Fortunately this +    // constructor is guaranteed to be called before any subclass constructor. +    // Make this new instance the currently-initializing LLSingleton.      LLSingleton_manage_master<DERIVED_TYPE>().push_initializing(this);  } +// forward declare for friend directive within LLSingleton +template <typename DERIVED_TYPE> +class LLParamSingleton; +  /**   * LLSingleton implements the getInstance() method part of the Singleton   * pattern. It can't make the derived class constructors protected, though, so @@ -232,10 +252,19 @@ LLSingletonBase::LLSingletonBase(tag<DERIVED_TYPE>):   * leading back to yours, move the instance reference from your constructor to   * your initSingleton() method.   * - * If you override LLSingleton<T>::cleanupSingleton(), your method will be - * called if someone calls LLSingletonBase::cleanupAll(). The significant part - * of this promise is that cleanupAll() will call individual - * cleanupSingleton() methods in reverse dependency order. + * If you override LLSingleton<T>::cleanupSingleton(), your method will + * implicitly be called by LLSingleton<T>::deleteSingleton() just before the + * instance is destroyed. We introduce a special cleanupSingleton() method + * because cleanupSingleton() operations can involve nontrivial realtime, or + * throw an exception. A destructor should do neither! + * + * If your cleanupSingleton() method throws an exception, we log that + * exception but carry on. + * + * If at some point you call LLSingletonBase::deleteAll(), all remaining + * LLSingleton<T> instances will be destroyed in reverse dependency order. (Or + * call MySubclass::deleteSingleton() to specifically destroy the canonical + * MySubclass instance.)   *   * That is, consider LLSingleton subclasses C, B and A. A depends on B, which   * in turn depends on C. These dependencies are expressed as calls to @@ -243,36 +272,116 @@ LLSingletonBase::LLSingletonBase(tag<DERIVED_TYPE>):   * It shouldn't matter whether these calls appear in A::A() or   * A::initSingleton(), likewise B::B() or B::initSingleton().   * - * We promise that if you later call LLSingletonBase::cleanupAll(): - * 1. A::cleanupSingleton() will be called before - * 2. B::cleanupSingleton(), which will be called before - * 3. C::cleanupSingleton(). + * We promise that if you later call LLSingletonBase::deleteAll(): + * 1. A::deleteSingleton() will be called before + * 2. B::deleteSingleton(), which will be called before + * 3. C::deleteSingleton().   * Put differently, if your LLSingleton subclass constructor or   * initSingleton() method explicitly depends on some other LLSingleton   * subclass, you may continue to rely on that other subclass in your   * cleanupSingleton() method. - * - * We introduce a special cleanupSingleton() method because cleanupSingleton() - * operations can involve nontrivial realtime, or might throw an exception. A - * destructor should do neither! - * - * If your cleanupSingleton() method throws an exception, we log that - * exception but proceed with the remaining cleanupSingleton() calls. - * - * Similarly, if at some point you call LLSingletonBase::deleteAll(), all - * remaining LLSingleton instances will be destroyed in dependency order. (Or - * call MySubclass::deleteSingleton() to specifically destroy the canonical - * MySubclass instance.) - * - * As currently written, LLSingleton is not thread-safe.   */  template <typename DERIVED_TYPE>  class LLSingleton : public LLSingletonBase  {  private: -    static DERIVED_TYPE* constructSingleton() +    // LLSingleton<DERIVED_TYPE> must have a distinct instance of +    // SingletonData for every distinct DERIVED_TYPE. It's tempting to +    // consider hoisting SingletonData up into LLSingletonBase. Don't do it. +    struct SingletonData      { -        return new DERIVED_TYPE(); +        // Use a recursive_mutex in case of constructor circularity. With a +        // non-recursive mutex, that would result in deadlock. +        typedef std::recursive_mutex mutex_t; +        mutex_t mMutex;             // LockStatic looks for mMutex + +        EInitState      mInitState{UNINITIALIZED}; +        DERIVED_TYPE*   mInstance{nullptr}; +    }; +    typedef llthread::LockStatic<SingletonData> LockStatic; + +    // Allow LLParamSingleton subclass -- but NOT DERIVED_TYPE itself -- to +    // access our private members. +    friend class LLParamSingleton<DERIVED_TYPE>; + +    // LLSingleton only supports a nullary constructor. However, the specific +    // purpose for its subclass LLParamSingleton is to support Singletons +    // requiring constructor arguments. constructSingleton() supports both use +    // cases. +    // Accepting LockStatic& requires that the caller has already locked our +    // static data before calling. +    template <typename... Args> +    static void constructSingleton(LockStatic& lk, Args&&... args) +    { +        auto prev_size = LLSingleton_manage_master<DERIVED_TYPE>().get_initializing_size(); +        // Any getInstance() calls after this point are from within constructor +        lk->mInitState = CONSTRUCTING; +        try +        { +            lk->mInstance = new DERIVED_TYPE(std::forward<Args>(args)...); +        } +        catch (const std::exception& err) +        { +            // LLSingletonBase might -- or might not -- have pushed the new +            // instance onto the init stack before the exception. Reset the +            // init stack to its previous size BEFORE logging so log-machinery +            // LLSingletons don't record a dependency on DERIVED_TYPE! +            LLSingleton_manage_master<DERIVED_TYPE>().reset_initializing(prev_size); +            logwarns({"Error constructing ", classname<DERIVED_TYPE>(), +                     ": ", err.what()}); +            // There isn't a separate EInitState value meaning "we attempted +            // to construct this LLSingleton subclass but could not," so use +            // DELETED. That seems slightly more appropriate than UNINITIALIZED. +            lk->mInitState = DELETED; +            // propagate the exception +            throw; +        } + +        // Any getInstance() calls after this point are from within initSingleton() +        lk->mInitState = INITIALIZING; +        try +        { +            // initialize singleton after constructing it so that it can +            // reference other singletons which in turn depend on it, thus +            // breaking cyclic dependencies +            lk->mInstance->initSingleton(); +            lk->mInitState = INITIALIZED; + +            // pop this off stack of initializing singletons +            pop_initializing(lk->mInstance); +        } +        catch (const std::exception& err) +        { +            // pop this off stack of initializing singletons here, too -- +            // BEFORE logging, so log-machinery LLSingletons don't record a +            // dependency on DERIVED_TYPE! +            pop_initializing(lk->mInstance); +            logwarns({"Error in ", classname<DERIVED_TYPE>(), +                     "::initSingleton(): ", err.what()}); +            // Get rid of the instance entirely. This call depends on our +            // recursive_mutex. We could have a deleteSingleton(LockStatic&) +            // overload and pass lk, but we don't strictly need it. +            deleteSingleton(); +            // propagate the exception +            throw; +        } +    } + +    static void pop_initializing(LLSingletonBase* sb) +    { +        // route through LLSingleton_manage_master so we Do The Right Thing +        // (namely, nothing) for MasterList +        LLSingleton_manage_master<DERIVED_TYPE>().pop_initializing(sb); +    } + +    static void capture_dependency(LLSingletonBase* sb) +    { +        // 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 +        // getInstance() was called by another LLSingleton, rather than from +        // vanilla application code, record the dependency. +        LLSingleton_manage_master<DERIVED_TYPE>().capture_dependency(sb);      }      // We know of no way to instruct the compiler that every subclass @@ -285,37 +394,6 @@ private:      // subclass body.      virtual void you_must_use_LLSINGLETON_macro() = 0; -    // stores pointer to singleton instance -    struct SingletonLifetimeManager -    { -        SingletonLifetimeManager() -        { -            construct(); -        } - -        static void construct() -        { -            sData.mInitState = CONSTRUCTING; -            sData.mInstance = constructSingleton(); -            sData.mInitState = INITIALIZING; -        } - -        ~SingletonLifetimeManager() -        { -            // The dependencies between LLSingletons, and the arbitrary order -            // of static-object destruction, mean that we DO NOT WANT this -            // destructor to delete this LLSingleton. This destructor will run -            // without regard to any other LLSingleton whose cleanup might -            // depend on its existence. If you want to clean up LLSingletons, -            // call LLSingletonBase::deleteAll() sometime before static-object -            // destruction begins. That method will properly honor cross- -            // LLSingleton dependencies. Otherwise we simply leak LLSingleton -            // instances at shutdown. Since the whole process is terminating -            // anyway, that's not necessarily a bad thing; it depends on what -            // resources your LLSingleton instances are managing. -        } -    }; -  protected:      // Pass DERIVED_TYPE explicitly to LLSingletonBase's constructor because,      // until our subclass constructor completes, *this isn't yet a @@ -330,102 +408,176 @@ protected:          LLSingleton_manage_master<DERIVED_TYPE>().add(this);      } -public: +protected:      virtual ~LLSingleton()      { -        // remove this instance from the master list +        // This phase of cleanup is performed in the destructor rather than in +        // deleteSingleton() to defend against manual deletion. When we moved +        // cleanup to deleteSingleton(), we hit crashes due to dangling +        // pointers in the MasterList. +        LockStatic lk; +        lk->mInstance  = nullptr; +        lk->mInitState = DELETED; + +        // Remove this instance from the master list.          LLSingleton_manage_master<DERIVED_TYPE>().remove(this); -        sData.mInstance = NULL; -        sData.mInitState = DELETED;      } +public:      /** -     * @brief Immediately delete the singleton. +     * @brief Cleanup and destroy the singleton instance.       * -     * A subsequent call to LLProxy::getInstance() will construct a new -     * instance of the class. +     * deleteSingleton() calls this instance's cleanupSingleton() method and +     * then destroys the instance.       * -     * 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. +     * A subsequent call to LLSingleton<T>::getInstance() will construct a new +     * instance of the class.       * -     * 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. +     * Without an explicit call to LLSingletonBase::deleteAll(), or +     * LLSingleton<T>::deleteSingleton(), LLSingleton instances are simply +     * leaked. (Allowing implicit destruction at shutdown caused too many +     * problems.)       */      static void deleteSingleton()      { -        delete sData.mInstance; -        sData.mInstance = NULL; -        sData.mInitState = DELETED; +        // Hold the lock while we call cleanupSingleton() and the destructor. +        // Our destructor also instantiates LockStatic, requiring a recursive +        // mutex. +        LockStatic lk; +        // of course, only cleanup and delete if there's something there +        if (lk->mInstance) +        { +            lk->mInstance->cleanup_(); +            delete lk->mInstance; +            // destructor clears mInstance (and mInitState) +        }      }      static DERIVED_TYPE* getInstance()      { -        static SingletonLifetimeManager sLifeTimeMgr; - -        switch (sData.mInitState) -        { -        case UNINITIALIZED: -            // should never be uninitialized at this point -            logerrs("Uninitialized 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: -            // go ahead and flag ourselves as initialized so we can be -            // reentrant during initialization -            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); -            break; - -        case INITIALIZED: -            break; - -        case DELETED: -            logwarns("Trying to access deleted singleton ", -                     demangle(typeid(DERIVED_TYPE).name()).c_str(), -                     " -- creating new instance"); -            SingletonLifetimeManager::construct(); -            // same as first time construction -            sData.mInitState = INITIALIZED;  -            sData.mInstance->initSingleton();  -            // pop this off stack of initializing singletons -            LLSingleton_manage_master<DERIVED_TYPE>().pop_initializing(sData.mInstance); -            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; +        // We know the viewer has LLSingleton dependency circularities. If you +        // feel strongly motivated to eliminate them, cheers and good luck. +        // (At that point we could consider a much simpler locking mechanism.) + +        // If A and B depend on each other, and thread T1 requests A at the +        // same moment thread T2 requests B, you could get a sequence like this: +        // - T1 locks A +        // - T2 locks B +        // - T1, having constructed A, calls A::initSingleton(), which calls +        //   B::getInstance() and blocks on B's lock +        // - T2, having constructed B, calls B::initSingleton(), which calls +        //   A::getInstance() and blocks on A's lock +        // In other words, classic deadlock. + +        // Avoid that by constructing and initializing every LLSingleton on +        // the main thread. In that scenario: +        // - T1 locks A +        // - T2 locks B +        // - T1 discovers A is UNINITIALIZED, so it queues a task for the main +        //   thread, unlocks A and blocks on the std::future. +        // - T2 discovers B is UNINITIALIZED, so it queues a task for the main +        //   thread, unlocks B and blocks on the std::future. +        // - The main thread executes T1's request for A. It locks A and +        //   starts to construct it. +        // - A::initSingleton() calls B::getInstance(). Fine: nobody's holding +        //   B's lock. +        // - The main thread locks B, constructs B, calls B::initSingleton(), +        //   which calls A::getInstance(), which returns A. +        // - B::getInstance() returns B to A::initSingleton(), unlocking B. +        // - A::getInstance() returns A to the task wrapper, unlocking A. +        // - The task wrapper passes A to T1 via the future. T1 resumes. +        // - The main thread executes T2's request for B. Oh look, B already +        //   exists. The task wrapper passes B to T2 via the future. T2 +        //   resumes. +        // This still works even if one of T1 or T2 *is* the main thread. +        // This still works even if thread T3 requests B at the same moment as +        // T2. Finding B still UNINITIALIZED, T3 also queues a task for the +        // main thread, unlocks B and blocks on a (distinct) std::future. By +        // the time the main thread executes T3's request for B, B already +        // exists, and is simply delivered via the future. + +        { // nested scope for 'lk' +            // In case racing threads call getInstance() at the same moment, +            // serialize the calls. +            LockStatic lk; + +            switch (lk->mInitState) +            { +            case CONSTRUCTING: +                // here if DERIVED_TYPE's constructor (directly or indirectly) +                // calls DERIVED_TYPE::getInstance() +                logerrs({"Tried to access singleton ", +                        classname<DERIVED_TYPE>(), +                        " from singleton constructor!"}); +                return nullptr; + +            case INITIALIZING: +                // here if DERIVED_TYPE::initSingleton() (directly or indirectly) +                // calls DERIVED_TYPE::getInstance(): go ahead and allow it +            case INITIALIZED: +                // normal subsequent calls +                // record the dependency, if any: check if we got here from another +                // LLSingleton's constructor or initSingleton() method +                capture_dependency(lk->mInstance); +                return lk->mInstance; + +            case DELETED: +                // called after deleteSingleton() +                logwarns({"Trying to access deleted singleton ", +                         classname<DERIVED_TYPE>(), +                         " -- creating new instance"}); +                // fall through +            case UNINITIALIZED: +            case QUEUED: +                // QUEUED means some secondary thread has already requested an +                // instance, but for present purposes that's semantically +                // identical to UNINITIALIZED: either way, we must ourselves +                // request an instance. +                break; +            } + +            // Here we need to construct a new instance. +            if (on_main_thread()) +            { +                // On the main thread, directly construct the instance while +                // holding the lock. +                constructSingleton(lk); +                capture_dependency(lk->mInstance); +                return lk->mInstance; +            } + +            // Here we need to construct a new instance, but we're on a secondary +            // thread. +            lk->mInitState = QUEUED; +        } // unlock 'lk' + +        // Per the comment block above, dispatch to the main thread. +        loginfos({classname<DERIVED_TYPE>(), +                 "::getInstance() dispatching to main thread"}); +        auto instance = LLMainThreadTask::dispatch( +            [](){ +                // VERY IMPORTANT to call getInstance() on the main thread, +                // rather than going straight to constructSingleton()! +                // During the time window before mInitState is INITIALIZED, +                // multiple requests might be queued. It's essential that, as +                // the main thread processes them, only the FIRST such request +                // actually constructs the instance -- every subsequent one +                // simply returns the existing instance. +                loginfos({classname<DERIVED_TYPE>(), +                         "::getInstance() on main thread"}); +                return getInstance(); +            }); +        // record the dependency chain tracked on THIS thread, not the main +        // thread (consider a getInstance() overload with a tag param that +        // suppresses dep tracking when dispatched to the main thread) +        capture_dependency(instance); +        loginfos({classname<DERIVED_TYPE>(), +                 "::getInstance() returning on requesting thread"}); +        return instance;      }      // Reference version of getInstance() -    // Preferred over getInstance() as it disallows checking for NULL +    // Preferred over getInstance() as it disallows checking for nullptr      static DERIVED_TYPE& instance()      {          return *getInstance(); @@ -435,7 +587,9 @@ public:      // Use this to avoid accessing singletons before they can safely be constructed.      static bool instanceExists()      { -        return sData.mInitState == INITIALIZED; +        // defend any access to sData from racing threads +        LockStatic lk; +        return lk->mInitState == INITIALIZED;      }      // Has this singleton been deleted? This can be useful during shutdown @@ -443,22 +597,181 @@ public:      // cleaned up.      static bool wasDeleted()      { -        return sData.mInitState == DELETED; +        // defend any access to sData from racing threads +        LockStatic lk; +        return lk->mInitState == DELETED;      } +}; + +/** + * LLParamSingleton<T> is like LLSingleton<T>, except in the following ways: + * + * * It is NOT instantiated on demand (instance() or getInstance()). You must + *   first call initParamSingleton(constructor args...). + * * Before initParamSingleton(), calling instance() or getInstance() dies with + *   LL_ERRS. + * * initParamSingleton() may be called only once. A second call dies with + *   LL_ERRS. + * * However, distinct initParamSingleton() calls can be used to engage + *   different constructors, as long as only one such call is executed at + *   runtime. + * * Unlike LLSingleton, an LLParamSingleton cannot be "revived" by an + *   instance() or getInstance() call after deleteSingleton(). + * + * Importantly, though, each LLParamSingleton subclass does participate in the + * dependency-ordered LLSingletonBase::deleteAll() processing. + */ +template <typename DERIVED_TYPE> +class LLParamSingleton : public LLSingleton<DERIVED_TYPE> +{  private: -    struct SingletonData +    typedef LLSingleton<DERIVED_TYPE> super; +    using typename super::LockStatic; + +    // Passes arguments to DERIVED_TYPE's constructor and sets appropriate +    // states, returning a pointer to the new instance. +    template <typename... Args> +    static DERIVED_TYPE* initParamSingleton_(Args&&... args)      { -        // 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; +        // In case racing threads both call initParamSingleton() at the same +        // time, serialize them. One should initialize; the other should see +        // mInitState already set. +        LockStatic lk; +        // For organizational purposes this function shouldn't be called twice +        if (lk->mInitState != super::UNINITIALIZED) +        { +            super::logerrs({"Tried to initialize singleton ", +                           super::template classname<DERIVED_TYPE>(), +                           " twice!"}); +            return nullptr; +        } +        else if (on_main_thread()) +        { +            // on the main thread, simply construct instance while holding lock +            super::logdebugs({super::template classname<DERIVED_TYPE>(), +                             "::initParamSingleton()"}); +            super::constructSingleton(lk, std::forward<Args>(args)...); +            return lk->mInstance; +        } +        else +        { +            // on secondary thread, dispatch to main thread -- +            // set state so we catch any other calls before the main thread +            // picks up the task +            lk->mInitState = super::QUEUED; +            // very important to unlock here so main thread can actually process +            lk.unlock(); +            super::loginfos({super::template classname<DERIVED_TYPE>(), +                            "::initParamSingleton() dispatching to main thread"}); +            // Normally it would be the height of folly to reference-bind +            // 'args' into a lambda to be executed on some other thread! By +            // the time that thread executed the lambda, the references would +            // all be dangling, and Bad Things would result. But +            // LLMainThreadTask::dispatch() promises to block until the passed +            // task has completed. So in this case we know the references will +            // remain valid until the lambda has run, so we dare to bind +            // references. +            auto instance = LLMainThreadTask::dispatch( +                [&](){ +                    super::loginfos({super::template classname<DERIVED_TYPE>(), +                                    "::initParamSingleton() on main thread"}); +                    return initParamSingleton_(std::forward<Args>(args)...); +                }); +            super::loginfos({super::template classname<DERIVED_TYPE>(), +                            "::initParamSingleton() returning on requesting thread"}); +            return instance; +        } +    } + +public: +    using super::deleteSingleton; +    using super::instanceExists; +    using super::wasDeleted; + +    /// initParamSingleton() constructs the instance, returning a reference. +    /// Pass whatever arguments are required to construct DERIVED_TYPE. +    template <typename... Args> +    static DERIVED_TYPE& initParamSingleton(Args&&... args) +    { +        return *initParamSingleton_(std::forward<Args>(args)...); +    } + +    static DERIVED_TYPE* getInstance() +    { +        // In case racing threads call getInstance() at the same moment as +        // initParamSingleton(), serialize the calls. +        LockStatic lk; + +        switch (lk->mInitState) +        { +        case super::UNINITIALIZED: +        case super::QUEUED: +            super::logerrs({"Uninitialized param singleton ", +                           super::template classname<DERIVED_TYPE>()}); +            break; + +        case super::CONSTRUCTING: +            super::logerrs({"Tried to access param singleton ", +                           super::template classname<DERIVED_TYPE>(), +                           " from singleton constructor!"}); +            break; + +        case super::INITIALIZING: +            // As with LLSingleton, explicitly permit circular calls from +            // within initSingleton() +        case super::INITIALIZED: +            // for any valid call, capture dependencies +            super::capture_dependency(lk->mInstance); +            return lk->mInstance; + +        case super::DELETED: +            super::logerrs({"Trying to access deleted param singleton ", +                           super::template classname<DERIVED_TYPE>()}); +            break; +        } + +        // should never actually get here; this is to pacify the compiler, +        // which assumes control might return from logerrs() +        return nullptr; +    } + +    // instance() is replicated here so it calls +    // LLParamSingleton::getInstance() rather than LLSingleton::getInstance() +    // -- avoid making getInstance() virtual +    static DERIVED_TYPE& instance() +    { +        return *getInstance(); +    }  }; -template<typename T> -typename LLSingleton<T>::SingletonData LLSingleton<T>::sData; +/** + * Initialization locked singleton, only derived class can decide when to initialize. + * Starts locked. + * For cases when singleton has a dependency onto something or. + * + * LLLockedSingleton is like an LLParamSingleton with a nullary constructor. + * It cannot be instantiated on demand (instance() or getInstance() call) -- + * it must be instantiated by calling construct(). However, it does + * participate in dependency-ordered LLSingletonBase::deleteAll() processing. + */ +template <typename DT> +class LLLockedSingleton : public LLParamSingleton<DT> +{ +    typedef LLParamSingleton<DT> super; + +public: +    using super::deleteSingleton; +    using super::getInstance; +    using super::instance; +    using super::instanceExists; +    using super::wasDeleted; + +    static DT* construct() +    { +        return super::initParamSingleton(); +    } +};  /**   * Use LLSINGLETON(Foo); at the start of an LLSingleton<Foo> subclass body @@ -484,12 +797,23 @@ typename LLSingleton<T>::SingletonData LLSingleton<T>::sData;   * file, use 'inline' (unless it's a template class) to avoid duplicate-symbol   * errors at link time.   */ -#define LLSINGLETON(DERIVED_CLASS)                                      \ +#define LLSINGLETON(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 LLSingleton<DERIVED_CLASS>;                            \ +    DERIVED_CLASS(__VA_ARGS__) + +/** + * A slight variance from the above, but includes the "override" keyword + */ +#define LLSINGLETON_C11(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() override {}           \ +    friend class LLSingleton<DERIVED_CLASS>;                            \      DERIVED_CLASS()  /** @@ -510,4 +834,8 @@ private:                                                                \      /* LLSINGLETON() is carefully implemented to permit exactly this */ \      LLSINGLETON(DERIVED_CLASS) {} +#define LLSINGLETON_EMPTY_CTOR_C11(DERIVED_CLASS)                       \ +    /* LLSINGLETON() is carefully implemented to permit exactly this */ \ +    LLSINGLETON_C11(DERIVED_CLASS) {} +  #endif  | 
