summaryrefslogtreecommitdiff
path: root/indra/llcommon/llsingleton.cpp
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2019-08-19 11:44:56 -0400
committerNat Goodspeed <nat@lindenlab.com>2019-08-19 11:44:56 -0400
commit310db14beefc29ee72e0d13f0cb63cb2958ebf68 (patch)
treece7f595cd9b08fba9bb6eafd6a4f51f5f1240b4e /indra/llcommon/llsingleton.cpp
parentd10b06dc4fd69a8b9bc0baf42f88f88c39ed17e4 (diff)
DRTVWR-493: Improve exception safety of LLSingleton initialization.
Add try/catch clauses to constructSingleton() (to catch exceptions in the subclass constructor) and finishInitializing() (to catch exceptions in the subclass initSingleton() method). Each of these catch clauses rethrows the exception -- they're for cleanup, not for ultimate handling. Introduce LLSingletonBase::reset_initializing(list_t::size_t). The idea is that since we can't know whether the exception happened before or after the push_initializing() call in LLSingletonBase's constructor, we can't just pop the stack. Instead, constructSingleton() captures the stack size before attempting to construct the new LLSingleton subclass. On exception, it calls reset_initializing() to restore the stack to that size. Naturally that requires a corresponding LLSingleton_manage_master method, whose MasterList specialization is a no-op. finishInitializing()'s exception handling is a bit simpler because it has a constructed LLSingleton subclass instance in hand, therefore push_initializing() has definitely been called, therefore it can call pop_initializing(). Break out new static capture_dependency() method from finishInitializing() because, in the previous LLSingleton::getInstance() implementation, the logic now wrapped in capture_dependency() was reached even in the INITIALIZED case. TODO: Add a new EInitState to differentiate "have been constructed, now calling initSingleton()" from "fully initialized, normal case" -- in the latter control path we should not be calling capture_dependency(). The LLSingleton_manage_master<LLSingletonBase::MasterList> specialization's get_initializing() function (which called get_initializing_from()) was potentially dangerous. get_initializing() is called by push_initializing(), which (in the general case) is called by LLSingletonBase's constructor. If somehow the MasterList's LLSingletonBase constructor ended up calling get_initializing(), it would have called get_initializing_from(), passing an LLSingletonBase which had not yet been constructed into the MasterList. In particular, its mInitializing map would not yet have been initialized at all. Since the MasterList must not, by design, depend on any other LLSingletons, LLSingleton_manage_master<LLSingletonBase::MasterList>::get_initializing() need not return a list from the official mInitializing map anyway. It can, and should, and now does, return a static dummy list. That obviates get_initializing_from(), which is removed. That in turn means we no longer need to pass get_initializing() an LLSingletonBase*. Remove that parameter.
Diffstat (limited to 'indra/llcommon/llsingleton.cpp')
-rw-r--r--indra/llcommon/llsingleton.cpp31
1 files changed, 25 insertions, 6 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)
{