summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--indra/llcommon/llsingleton.cpp31
-rw-r--r--indra/llcommon/llsingleton.h97
2 files changed, 106 insertions, 22 deletions
diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp
index 9fbd78a000..adf72bf700 100644
--- a/indra/llcommon/llsingleton.cpp
+++ b/indra/llcommon/llsingleton.cpp
@@ -134,12 +134,6 @@ LLSingletonBase::list_t& LLSingletonBase::get_initializing()
return LLSingletonBase::MasterList::instance().get_initializing_();
}
-//static
-LLSingletonBase::list_t& LLSingletonBase::get_initializing_from(MasterList* master)
-{
- return master->get_initializing_();
-}
-
LLSingletonBase::~LLSingletonBase() {}
void LLSingletonBase::push_initializing(const char* name)
@@ -186,6 +180,31 @@ void LLSingletonBase::pop_initializing()
log_initializing("Popping", typeid(*back).name());
}
+void LLSingletonBase::reset_initializing(list_t::size_type size)
+{
+ // called for cleanup in case the LLSingleton subclass constructor throws
+ // an exception
+
+ // The tricky thing about this, the reason we have a separate method
+ // instead of just calling pop_initializing(), is (hopefully remote)
+ // possibility that the exception happened *before* the
+ // push_initializing() call in LLSingletonBase's constructor. So only
+ // remove the stack top if in fact we've pushed something more than the
+ // previous size.
+ list_t& list(get_initializing());
+
+ while (list.size() > size)
+ {
+ list.pop_back();
+ }
+
+ // as in pop_initializing()
+ if (list.empty())
+ {
+ MasterList::instance().cleanup_initializing_();
+ }
+}
+
//static
void LLSingletonBase::log_initializing(const char* verb, const char* name)
{
diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h
index e0d75ed72a..de6990efd4 100644
--- a/indra/llcommon/llsingleton.h
+++ b/indra/llcommon/llsingleton.h
@@ -55,7 +55,6 @@ private:
// 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*);
// Produce a vector<LLSingletonBase*> of master list, in dependency order.
typedef std::vector<LLSingletonBase*> vec_t;
static vec_t dep_sort();
@@ -112,6 +111,9 @@ protected:
// That being the case, we control exactly when it happens -- and we can
// pop the stack immediately thereafter.
void pop_initializing();
+ // Remove 'this' from the init stack in case of exception in the
+ // LLSingleton subclass constructor.
+ static void reset_initializing(list_t::size_type size);
private:
// logging
static void log_initializing(const char* verb, const char* name);
@@ -190,7 +192,15 @@ 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 init
+ // stack from the MasterList singleton instance.
+ LLSingletonBase::list_t& get_initializing() { return LLSingletonBase::get_initializing(); }
};
// But for the specific case of LLSingletonBase::MasterList, don't.
@@ -201,9 +211,14 @@ 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)
+ // since we never pushed, no need to clean up
+ void reset_initializing(LLSingletonBase::list_t::size_type size) {}
+ LLSingletonBase::list_t& get_initializing()
{
- return LLSingletonBase::get_initializing_from(instance);
+ // The MasterList shouldn't depend on any other LLSingletons. We'd
+ // get into trouble if we tried to recursively engage that machinery.
+ static LLSingletonBase::list_t sDummyList;
+ return sDummyList;
}
};
@@ -213,7 +228,12 @@ LLSingletonBase::LLSingletonBase(tag<DERIVED_TYPE>):
mCleaned(false),
mDeleteSingleton(NULL)
{
- // 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);
}
@@ -298,8 +318,27 @@ private:
static void constructSingleton(Args&&... args)
{
sData.mInitState = CONSTRUCTING;
- sData.mInstance = new DERIVED_TYPE(std::forward<Args>(args)...);
- sData.mInitState = INITIALIZING;
+ auto prev_size = LLSingleton_manage_master<DERIVED_TYPE>().get_initializing().size();
+ try
+ {
+ sData.mInstance = new DERIVED_TYPE(std::forward<Args>(args)...);
+ sData.mInitState = INITIALIZING;
+ }
+ catch (const std::exception& err)
+ {
+ logwarns("Error constructing ", demangle(typeid(DERIVED_TYPE).name()).c_str(),
+ ": ", 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.
+ sData.mInitState = DELETED;
+ // 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.
+ LLSingleton_manage_master<DERIVED_TYPE>().reset_initializing(prev_size);
+ // propagate the exception
+ throw;
+ }
}
static void finishInitializing()
@@ -310,16 +349,41 @@ private:
// 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);
-
- // The remaining 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.
+ try
+ {
+ sData.mInstance->initSingleton();
+
+ // pop this off stack of initializing singletons
+ LLSingleton_manage_master<DERIVED_TYPE>().pop_initializing(sData.mInstance);
+
+ // record the dependency, if any
+ capture_dependency();
+ }
+ catch (const std::exception& err)
+ {
+ logwarns("Error in ", demangle(typeid(DERIVED_TYPE).name()).c_str(),
+ "::initSingleton(): ", err.what());
+ // pop this off stack of initializing singletons here, too
+ LLSingleton_manage_master<DERIVED_TYPE>().pop_initializing(sData.mInstance);
+ // and get rid of the instance entirely
+ deleteSingleton();
+ // propagate the exception
+ throw;
+ }
+ }
+
+ // Without this 'using' declaration, the static method we're declaring
+ // here would hide the base-class method we want it to call.
+ using LLSingletonBase::capture_dependency;
+ static void capture_dependency()
+ {
+ // 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.
sData.mInstance->capture_dependency(
- LLSingleton_manage_master<DERIVED_TYPE>().get_initializing(sData.mInstance),
+ LLSingleton_manage_master<DERIVED_TYPE>().get_initializing(),
sData.mInitState);
}
@@ -427,6 +491,7 @@ public:
case INITIALIZED:
// normal subsequent calls
+ capture_dependency();
break;
case DELETED: