Skip to content
Snippets Groups Projects
Commit 687efd84 authored by Nat Goodspeed's avatar Nat Goodspeed
Browse files

MAINT-5232: Loosen LLSingleton circularity constraints slightly.

LLSingleton explicitly supports circular dependencies: initialization
performed during an LLSingleton subclass's initSingleton() method may
recursively call that same subclass's getInstance() method. On the other hand,
circularity from a subclass constructor cannot be permitted, else
getInstance() would have to return a partially-constructed object.
Our dependency tracking circularity check initially forbade both. Loosen it to
permit references from within initSingleton().
parent aefdba12
Branches
Tags
No related merge requests found
...@@ -123,7 +123,7 @@ void LLSingletonBase::pop_initializing() ...@@ -123,7 +123,7 @@ void LLSingletonBase::pop_initializing()
list.pop_back(); list.pop_back();
} }
void LLSingletonBase::capture_dependency() void LLSingletonBase::capture_dependency(EInitState initState)
{ {
// Did this getInstance() call come from another LLSingleton, or from // Did this getInstance() call come from another LLSingleton, or from
// vanilla application code? Note that although this is a nontrivial // vanilla application code? Note that although this is a nontrivial
...@@ -150,11 +150,18 @@ void LLSingletonBase::capture_dependency() ...@@ -150,11 +150,18 @@ void LLSingletonBase::capture_dependency()
// is the actual LLSingletonBase instance. // is the actual LLSingletonBase instance.
out << typeid(**found).name() << " -> "; out << typeid(**found).name() << " -> ";
} }
// DEBUGGING: Initially, make this crump. We want to know how bad // We promise to capture dependencies from both the constructor
// the problem is before we add it to the long, sad list of // and the initSingleton() method, so an LLSingleton's instance
// ominous warnings that everyone always ignores. // pointer is on the initializing list during both. Now that we've
logerrs("LLSingleton circularity: ", out.str().c_str(), // detected circularity, though, we must distinguish the two. If
typeid(*this).name()); // the recursive call is from the constructor, we CAN'T honor it:
// otherwise we'd be returning a pointer to a partially-
// constructed object! But from initSingleton() is okay: that
// method exists specifically to support circularity.
// Decide which log helper to call based on initState. They have
// identical signatures.
((initState == CONSTRUCTING)? logerrs : logwarns)
("LLSingleton circularity: ", out.str().c_str(), typeid(*this).name(), "");
} }
else else
{ {
......
...@@ -58,6 +58,15 @@ class LLSingletonBase: private boost::noncopyable ...@@ -58,6 +58,15 @@ class LLSingletonBase: private boost::noncopyable
set_t mDepends; set_t mDepends;
protected: protected:
typedef enum e_init_state
{
UNINITIALIZED = 0, // must be default-initialized state
CONSTRUCTING,
INITIALIZING,
INITIALIZED,
DELETED
} EInitState;
// Base-class constructor should only be invoked by the DERIVED_TYPE // Base-class constructor should only be invoked by the DERIVED_TYPE
// constructor. // constructor.
LLSingletonBase(); LLSingletonBase();
...@@ -87,7 +96,7 @@ class LLSingletonBase: private boost::noncopyable ...@@ -87,7 +96,7 @@ class LLSingletonBase: private boost::noncopyable
void pop_initializing(); void pop_initializing();
// If a given call to B::getInstance() happens during either A::A() or // If a given call to B::getInstance() happens during either A::A() or
// A::initSingleton(), record that A directly depends on B. // A::initSingleton(), record that A directly depends on B.
void capture_dependency(); void capture_dependency(EInitState);
// delegate LL_ERRS() logging to llsingleton.cpp // delegate LL_ERRS() logging to llsingleton.cpp
static void logerrs(const char* p1, const char* p2="", static void logerrs(const char* p1, const char* p2="",
...@@ -232,15 +241,6 @@ template <typename DERIVED_TYPE> ...@@ -232,15 +241,6 @@ template <typename DERIVED_TYPE>
class LLSingleton : public LLSingletonBase class LLSingleton : public LLSingletonBase
{ {
private: private:
typedef enum e_init_state
{
UNINITIALIZED = 0, // must be default-initialized state
CONSTRUCTING,
INITIALIZING,
INITIALIZED,
DELETED
} EInitState;
static DERIVED_TYPE* constructSingleton() static DERIVED_TYPE* constructSingleton()
{ {
return new DERIVED_TYPE(); return new DERIVED_TYPE();
...@@ -377,7 +377,7 @@ class LLSingleton : public LLSingletonBase ...@@ -377,7 +377,7 @@ class LLSingleton : public LLSingletonBase
// an LLSingleton that directly depends on DERIVED_TYPE. If this call // an LLSingleton that directly depends on DERIVED_TYPE. If this call
// came from another LLSingleton, rather than from vanilla application // came from another LLSingleton, rather than from vanilla application
// code, record the dependency. // code, record the dependency.
sData.mInstance->capture_dependency(); sData.mInstance->capture_dependency(sData.mInitState);
return sData.mInstance; return sData.mInstance;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment