Modernize LLSingleton
This commit is contained in:
@@ -28,5 +28,4 @@
|
||||
|
||||
#include "llsingleton.h"
|
||||
|
||||
std::map<std::string, void *> * LLSingletonRegistry::sSingletonMap = NULL;
|
||||
|
||||
|
||||
@@ -27,42 +27,9 @@
|
||||
|
||||
#include "llerror.h" // *TODO: eliminate this
|
||||
|
||||
#include <map>
|
||||
#include <typeinfo>
|
||||
#include <boost/noncopyable.hpp>
|
||||
|
||||
/// @brief A global registry of all singletons to prevent duplicate allocations
|
||||
/// across shared library boundaries
|
||||
class LL_COMMON_API LLSingletonRegistry {
|
||||
private:
|
||||
typedef std::map<std::string, void *> TypeMap;
|
||||
static TypeMap * sSingletonMap;
|
||||
|
||||
static void checkInit()
|
||||
{
|
||||
if(sSingletonMap == NULL)
|
||||
{
|
||||
sSingletonMap = new TypeMap();
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
template<typename T> static void * & get()
|
||||
{
|
||||
std::string name(typeid(T).name());
|
||||
|
||||
checkInit();
|
||||
|
||||
// the first entry of the pair returned by insert will be either the existing
|
||||
// iterator matching our key, or the newly inserted NULL initialized entry
|
||||
// see "Insert element" in http://www.sgi.com/tech/stl/UniqueAssociativeContainer.html
|
||||
TypeMap::iterator result =
|
||||
sSingletonMap->insert(std::make_pair(name, (void*)NULL)).first;
|
||||
|
||||
return result->second;
|
||||
}
|
||||
};
|
||||
|
||||
// LLSingleton implements the getInstance() method part of the Singleton
|
||||
// pattern. It can't make the derived class constructors protected, though, so
|
||||
// you have to do that yourself.
|
||||
@@ -101,21 +68,29 @@ private:
|
||||
DELETED
|
||||
} EInitState;
|
||||
|
||||
// stores pointer to singleton instance
|
||||
// and tracks initialization state of singleton
|
||||
struct SingletonInstanceData
|
||||
static DERIVED_TYPE* constructSingleton()
|
||||
{
|
||||
EInitState mInitState;
|
||||
DERIVED_TYPE* mSingletonInstance;
|
||||
|
||||
SingletonInstanceData()
|
||||
: mSingletonInstance(NULL),
|
||||
mInitState(UNINITIALIZED)
|
||||
{}
|
||||
return new DERIVED_TYPE();
|
||||
}
|
||||
|
||||
~SingletonInstanceData()
|
||||
// stores pointer to singleton instance
|
||||
struct SingletonLifetimeManager
|
||||
{
|
||||
SingletonLifetimeManager()
|
||||
{
|
||||
if (mInitState != DELETED)
|
||||
construct();
|
||||
}
|
||||
|
||||
static void construct()
|
||||
{
|
||||
sData.mInitState = CONSTRUCTING;
|
||||
sData.mInstance = constructSingleton();
|
||||
sData.mInitState = INITIALIZING;
|
||||
}
|
||||
|
||||
~SingletonLifetimeManager()
|
||||
{
|
||||
if (sData.mInitState != DELETED)
|
||||
{
|
||||
deleteSingleton();
|
||||
}
|
||||
@@ -125,9 +100,8 @@ private:
|
||||
public:
|
||||
virtual ~LLSingleton()
|
||||
{
|
||||
SingletonInstanceData& data = getData();
|
||||
data.mSingletonInstance = NULL;
|
||||
data.mInitState = DELETED;
|
||||
sData.mInstance = NULL;
|
||||
sData.mInitState = DELETED;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -152,37 +126,49 @@ public:
|
||||
*/
|
||||
static void deleteSingleton()
|
||||
{
|
||||
DERIVED_TYPE* instance = getData().mSingletonInstance;
|
||||
getData().mInitState = DELETED;
|
||||
getData().mSingletonInstance = NULL;
|
||||
delete instance;
|
||||
delete sData.mInstance;
|
||||
sData.mInstance = NULL;
|
||||
sData.mInitState = DELETED;
|
||||
}
|
||||
|
||||
static SingletonInstanceData& getData()
|
||||
{
|
||||
// this is static to cache the lookup results
|
||||
static void * & registry = LLSingletonRegistry::get<DERIVED_TYPE>();
|
||||
|
||||
// *TODO - look into making this threadsafe
|
||||
if(NULL == registry)
|
||||
{
|
||||
static SingletonInstanceData data;
|
||||
registry = &data;
|
||||
}
|
||||
|
||||
return *static_cast<SingletonInstanceData *>(registry);
|
||||
}
|
||||
|
||||
static DERIVED_TYPE* getInstance()
|
||||
{
|
||||
SingletonInstanceData& data = getData();
|
||||
static SingletonLifetimeManager sLifeTimeMgr;
|
||||
|
||||
if (data.mInitState != INITIALIZED)
|
||||
switch (sData.mInitState)
|
||||
{
|
||||
createInstance(data);
|
||||
case UNINITIALIZED:
|
||||
// should never be uninitialized at this point
|
||||
llassert(false);
|
||||
return NULL;
|
||||
case CONSTRUCTING:
|
||||
llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << LL_ENDL;
|
||||
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();
|
||||
return sData.mInstance;
|
||||
case INITIALIZED:
|
||||
return sData.mInstance;
|
||||
case DELETED:
|
||||
llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << LL_ENDL;
|
||||
SingletonLifetimeManager::construct();
|
||||
// same as first time construction
|
||||
sData.mInitState = INITIALIZED;
|
||||
sData.mInstance->initSingleton();
|
||||
return sData.mInstance;
|
||||
}
|
||||
|
||||
return data.mSingletonInstance;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static DERIVED_TYPE* getIfExists()
|
||||
{
|
||||
return sData.mInstance;
|
||||
}
|
||||
|
||||
// Reference version of getInstance()
|
||||
@@ -196,46 +182,31 @@ public:
|
||||
// Use this to avoid accessing singletons before the can safely be constructed
|
||||
static bool instanceExists()
|
||||
{
|
||||
return getData().mInitState == INITIALIZED;
|
||||
return sData.mInitState == INITIALIZED;
|
||||
}
|
||||
|
||||
// Has this singleton already been deleted?
|
||||
// Use this to avoid accessing singletons from a static object's destructor
|
||||
static bool destroyed()
|
||||
{
|
||||
return getData().mInitState == DELETED;
|
||||
return sData.mInitState == DELETED;
|
||||
}
|
||||
|
||||
private:
|
||||
static void createInstance(SingletonInstanceData& data);
|
||||
|
||||
virtual void initSingleton() {}
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
// Moved this here cause it's too big to be inlined --Aleric.
|
||||
template<typename DERIVED_TYPE>
|
||||
void LLSingleton<DERIVED_TYPE>::createInstance(SingletonInstanceData& data)
|
||||
{
|
||||
if (data.mInitState == CONSTRUCTING)
|
||||
{
|
||||
llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from singleton constructor!" << llendl;
|
||||
}
|
||||
|
||||
if (data.mInitState == DELETED)
|
||||
{
|
||||
llwarns << "Trying to access deleted singleton " << typeid(DERIVED_TYPE).name() << " creating new instance" << llendl;
|
||||
}
|
||||
|
||||
if (data.mInitState == INITIALIZING)
|
||||
{
|
||||
llerrs << "Tried to access singleton " << typeid(DERIVED_TYPE).name() << " from initSingleton(), using half-initialized object" << llendl;
|
||||
return;
|
||||
}
|
||||
|
||||
data.mInitState = CONSTRUCTING;
|
||||
data.mSingletonInstance = new DERIVED_TYPE();
|
||||
data.mInitState = INITIALIZING;
|
||||
data.mSingletonInstance->initSingleton();
|
||||
data.mInitState = INITIALIZED;
|
||||
}
|
||||
template<typename T>
|
||||
typename LLSingleton<T>::SingletonData LLSingleton<T>::sData;
|
||||
|
||||
#endif
|
||||
|
||||
Reference in New Issue
Block a user