Automated merge with ssh://bitbucket.org/lindenlab/viewer-release
commit
c92eb7e591
|
|
@ -42,6 +42,7 @@
|
|||
#include "lldiriterator.h"
|
||||
#include "v4coloru.h"
|
||||
#include "llsdserialize.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
// system libraries
|
||||
#include <iostream>
|
||||
|
|
@ -634,7 +635,7 @@ int main(int argc, char** argv)
|
|||
}
|
||||
|
||||
// Cleanup and exit
|
||||
LLImage::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLImage);
|
||||
if (fast_timer_log_thread)
|
||||
{
|
||||
fast_timer_log_thread->shutdown();
|
||||
|
|
|
|||
|
|
@ -167,6 +167,7 @@ set(llcommon_HEADER_FILES
|
|||
llhash.h
|
||||
llheartbeat.h
|
||||
llindexedvector.h
|
||||
llinitdestroyclass.h
|
||||
llinitparam.h
|
||||
llinstancetracker.h
|
||||
llkeythrottle.h
|
||||
|
|
@ -183,6 +184,7 @@ set(llcommon_HEADER_FILES
|
|||
llmortician.h
|
||||
llnametable.h
|
||||
llpointer.h
|
||||
llpounceable.h
|
||||
llpredicate.h
|
||||
llpreprocessor.h
|
||||
llpriqueuemap.h
|
||||
|
|
@ -327,6 +329,7 @@ if (LL_TESTS)
|
|||
LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
|
||||
LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}")
|
||||
|
||||
# *TODO - reenable these once tcmalloc libs no longer break the build.
|
||||
#ADD_BUILD_TEST(llallocator llcommon)
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
#include "lleventtimer.h"
|
||||
#include "google_breakpad/exception_handler.h"
|
||||
#include "stringize.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
//
|
||||
// Signal handling
|
||||
|
|
@ -177,7 +178,7 @@ LLApp::~LLApp()
|
|||
|
||||
if(mExceptionHandler != 0) delete mExceptionHandler;
|
||||
|
||||
LLCommon::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLCommon);
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* @file llcleanup.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2015-05-20
|
||||
* @brief Mechanism for cleaning up subsystem resources
|
||||
*
|
||||
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
|
||||
* Copyright (c) 2015, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLCLEANUP_H)
|
||||
#define LL_LLCLEANUP_H
|
||||
|
||||
#include "llerror.h"
|
||||
|
||||
// Instead of directly calling SomeClass::cleanupClass(), use
|
||||
// SUBSYSTEM_CLEANUP(SomeClass);
|
||||
// This logs the call as well as performing it. That gives us a baseline
|
||||
// subsystem shutdown order against which to compare subsequent dynamic
|
||||
// shutdown schemes.
|
||||
#define SUBSYSTEM_CLEANUP(CLASSNAME) \
|
||||
do { \
|
||||
LL_INFOS("Cleanup") << "Calling " #CLASSNAME "::cleanupClass()" << LL_ENDL; \
|
||||
CLASSNAME::cleanupClass(); \
|
||||
} while (0)
|
||||
// Use ancient do { ... } while (0) macro trick to permit a block of
|
||||
// statements with the same syntax as a single statement.
|
||||
|
||||
#endif /* ! defined(LL_LLCLEANUP_H) */
|
||||
|
|
@ -31,6 +31,7 @@
|
|||
#include "llthread.h"
|
||||
#include "lltrace.h"
|
||||
#include "lltracethreadrecorder.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
//static
|
||||
BOOL LLCommon::sAprInitialized = FALSE;
|
||||
|
|
@ -63,11 +64,11 @@ void LLCommon::cleanupClass()
|
|||
sMasterThreadRecorder = NULL;
|
||||
LLTrace::set_master_thread_recorder(NULL);
|
||||
LLThreadSafeRefCount::cleanupThreadSafeRefCount();
|
||||
LLTimer::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLTimer);
|
||||
if (sAprInitialized)
|
||||
{
|
||||
ll_cleanup_apr();
|
||||
sAprInitialized = FALSE;
|
||||
}
|
||||
LLMemory::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLMemory);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -238,6 +238,14 @@ namespace {
|
|||
namespace
|
||||
{
|
||||
std::string className(const std::type_info& type)
|
||||
{
|
||||
return LLError::Log::demangle(type.name());
|
||||
}
|
||||
} // anonymous
|
||||
|
||||
namespace LLError
|
||||
{
|
||||
std::string Log::demangle(const char* mangled)
|
||||
{
|
||||
#ifdef __GNUC__
|
||||
// GCC: type_info::name() returns a mangled class name,st demangle
|
||||
|
|
@ -252,18 +260,18 @@ namespace
|
|||
// but gcc 3.3 libstc++'s implementation of demangling is broken
|
||||
// and fails without.
|
||||
|
||||
char* name = abi::__cxa_demangle(type.name(),
|
||||
char* name = abi::__cxa_demangle(mangled,
|
||||
abi_name_buf, &abi_name_len, &status);
|
||||
// this call can realloc the abi_name_buf pointer (!)
|
||||
|
||||
return name ? name : type.name();
|
||||
return name ? name : mangled;
|
||||
|
||||
#elif LL_WINDOWS
|
||||
// DevStudio: type_info::name() includes the text "class " at the start
|
||||
|
||||
static const std::string class_prefix = "class ";
|
||||
|
||||
std::string name = type.name();
|
||||
std::string name = mangled;
|
||||
std::string::size_type p = name.find(class_prefix);
|
||||
if (p == std::string::npos)
|
||||
{
|
||||
|
|
@ -272,11 +280,14 @@ namespace
|
|||
|
||||
return name.substr(p + class_prefix.size());
|
||||
|
||||
#else
|
||||
return type.name();
|
||||
#else
|
||||
return mangled;
|
||||
#endif
|
||||
}
|
||||
} // LLError
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string functionName(const std::string& preprocessor_name)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
|
|
@ -403,6 +414,11 @@ namespace
|
|||
|
||||
namespace LLError
|
||||
{
|
||||
bool is_available()
|
||||
{
|
||||
return Globals::instanceExists();
|
||||
}
|
||||
|
||||
class SettingsConfig : public LLRefCount
|
||||
{
|
||||
friend class Settings;
|
||||
|
|
|
|||
|
|
@ -189,6 +189,7 @@ namespace LLError
|
|||
static std::ostringstream* out();
|
||||
static void flush(std::ostringstream* out, char* message);
|
||||
static void flush(std::ostringstream*, const CallSite&);
|
||||
static std::string demangle(const char* mangled);
|
||||
};
|
||||
|
||||
struct LL_COMMON_API CallSite
|
||||
|
|
|
|||
|
|
@ -189,6 +189,11 @@ namespace LLError
|
|||
|
||||
LL_COMMON_API std::string abbreviateFile(const std::string& filePath);
|
||||
LL_COMMON_API int shouldLogCallCount();
|
||||
|
||||
// Check whether Globals exists. This should only be used by LLSingleton
|
||||
// infrastructure to avoid trying to log when our internal LLSingleton is
|
||||
// unavailable -- circularity ensues.
|
||||
LL_COMMON_API bool is_available();
|
||||
};
|
||||
|
||||
#endif // LL_LLERRORCONTROL_H
|
||||
|
|
|
|||
|
|
@ -0,0 +1,190 @@
|
|||
/**
|
||||
* @file llinitdestroyclass.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2015-05-27
|
||||
* @brief LLInitClass / LLDestroyClass mechanism
|
||||
*
|
||||
* The LLInitClass template, extracted from llui.h, ensures that control will
|
||||
* reach a static initClass() method. LLDestroyClass does the same for a
|
||||
* static destroyClass() method.
|
||||
*
|
||||
* The distinguishing characteristics of these templates are:
|
||||
*
|
||||
* - All LLInitClass<T>::initClass() methods are triggered by an explicit call
|
||||
* to LLInitClassList::instance().fireCallbacks(). Presumably this call
|
||||
* happens sometime after all static objects in the program have been
|
||||
* initialized. In other words, each LLInitClass<T>::initClass() method
|
||||
* should be able to make some assumptions about global program state.
|
||||
*
|
||||
* - Similarly, LLDestroyClass<T>::destroyClass() methods are triggered by
|
||||
* LLDestroyClassList::instance().fireCallbacks(). Again, presumably this
|
||||
* happens at a well-defined moment in the program's shutdown sequence.
|
||||
*
|
||||
* - The initClass() calls happen in an unspecified sequence. You may not rely
|
||||
* on the relative ordering of LLInitClass<T>::initClass() versus another
|
||||
* LLInitClass<U>::initClass() method. If you need such a guarantee, use
|
||||
* LLSingleton instead and make the dependency explicit.
|
||||
*
|
||||
* - Similarly, LLDestroyClass<T>::destroyClass() may happen either before or
|
||||
* after LLDestroyClass<U>::destroyClass(). You cannot rely on that order.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
|
||||
* Copyright (c) 2015, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLINITDESTROYCLASS_H)
|
||||
#define LL_LLINITDESTROYCLASS_H
|
||||
|
||||
#include "llerror.h"
|
||||
#include "llsingleton.h"
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/signals2/signal.hpp>
|
||||
#include <typeinfo>
|
||||
|
||||
/**
|
||||
* LLCallbackRegistry is an implementation detail base class for
|
||||
* LLInitClassList and LLDestroyClassList. It's a very thin wrapper around a
|
||||
* Boost.Signals2 signal object.
|
||||
*/
|
||||
class LLCallbackRegistry
|
||||
{
|
||||
public:
|
||||
typedef boost::signals2::signal<void()> callback_signal_t;
|
||||
|
||||
void registerCallback(const callback_signal_t::slot_type& slot)
|
||||
{
|
||||
mCallbacks.connect(slot);
|
||||
}
|
||||
|
||||
void fireCallbacks()
|
||||
{
|
||||
mCallbacks();
|
||||
}
|
||||
|
||||
private:
|
||||
callback_signal_t mCallbacks;
|
||||
};
|
||||
|
||||
/**
|
||||
* LLInitClassList is the LLCallbackRegistry for LLInitClass. It stores the
|
||||
* registered initClass() methods. It must be an LLSingleton because
|
||||
* LLInitClass registers its initClass() method at static construction time
|
||||
* (before main()), requiring LLInitClassList to be fully constructed on
|
||||
* demand regardless of module initialization order.
|
||||
*/
|
||||
class LLInitClassList :
|
||||
public LLCallbackRegistry,
|
||||
public LLSingleton<LLInitClassList>
|
||||
{
|
||||
friend class LLSingleton<LLInitClassList>;
|
||||
private:
|
||||
LLInitClassList() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* LLDestroyClassList is the LLCallbackRegistry for LLDestroyClass. It stores
|
||||
* the registered destroyClass() methods. It must be an LLSingleton because
|
||||
* LLDestroyClass registers its destroyClass() method at static construction
|
||||
* time (before main()), requiring LLDestroyClassList to be fully constructed
|
||||
* on demand regardless of module initialization order.
|
||||
*/
|
||||
class LLDestroyClassList :
|
||||
public LLCallbackRegistry,
|
||||
public LLSingleton<LLDestroyClassList>
|
||||
{
|
||||
friend class LLSingleton<LLDestroyClassList>;
|
||||
private:
|
||||
LLDestroyClassList() {}
|
||||
};
|
||||
|
||||
/**
|
||||
* LLRegisterWith is an implementation detail for LLInitClass and
|
||||
* LLDestroyClass. It is intended to be used as a static class member whose
|
||||
* constructor registers the specified callback with the LLMumbleClassList
|
||||
* singleton registry specified as the template argument.
|
||||
*/
|
||||
template<typename T>
|
||||
class LLRegisterWith
|
||||
{
|
||||
public:
|
||||
LLRegisterWith(boost::function<void ()> func)
|
||||
{
|
||||
T::instance().registerCallback(func);
|
||||
}
|
||||
|
||||
// this avoids a MSVC bug where non-referenced static members are "optimized" away
|
||||
// even if their constructors have side effects
|
||||
S32 reference()
|
||||
{
|
||||
S32 dummy;
|
||||
dummy = 0;
|
||||
return dummy;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Derive MyClass from LLInitClass<MyClass> (the Curiously Recurring Template
|
||||
* Pattern) to ensure that the static method MyClass::initClass() will be
|
||||
* called (along with all other LLInitClass<T> subclass initClass() methods)
|
||||
* when someone calls LLInitClassList::instance().fireCallbacks(). This gives
|
||||
* the application specific control over the timing of all such
|
||||
* initializations, without having to insert calls for every such class into
|
||||
* generic application code.
|
||||
*/
|
||||
template<typename T>
|
||||
class LLInitClass
|
||||
{
|
||||
public:
|
||||
LLInitClass() { sRegister.reference(); }
|
||||
|
||||
// When this static member is initialized, the subclass initClass() method
|
||||
// is registered on LLInitClassList. See sRegister definition below.
|
||||
static LLRegisterWith<LLInitClassList> sRegister;
|
||||
private:
|
||||
|
||||
// Provide a default initClass() method in case subclass misspells (or
|
||||
// omits) initClass(). This turns a potential build error into a fatal
|
||||
// runtime error.
|
||||
static void initClass()
|
||||
{
|
||||
LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Derive MyClass from LLDestroyClass<MyClass> (the Curiously Recurring
|
||||
* Template Pattern) to ensure that the static method MyClass::destroyClass()
|
||||
* will be called (along with other LLDestroyClass<T> subclass destroyClass()
|
||||
* methods) when someone calls LLDestroyClassList::instance().fireCallbacks().
|
||||
* This gives the application specific control over the timing of all such
|
||||
* cleanup calls, without having to insert calls for every such class into
|
||||
* generic application code.
|
||||
*/
|
||||
template<typename T>
|
||||
class LLDestroyClass
|
||||
{
|
||||
public:
|
||||
LLDestroyClass() { sRegister.reference(); }
|
||||
|
||||
// When this static member is initialized, the subclass destroyClass()
|
||||
// method is registered on LLInitClassList. See sRegister definition
|
||||
// below.
|
||||
static LLRegisterWith<LLDestroyClassList> sRegister;
|
||||
private:
|
||||
|
||||
// Provide a default destroyClass() method in case subclass misspells (or
|
||||
// omits) destroyClass(). This turns a potential build error into a fatal
|
||||
// runtime error.
|
||||
static void destroyClass()
|
||||
{
|
||||
LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL;
|
||||
}
|
||||
};
|
||||
|
||||
// Here's where LLInitClass<T> specifies the subclass initClass() method.
|
||||
template <typename T> LLRegisterWith<LLInitClassList> LLInitClass<T>::sRegister(&T::initClass);
|
||||
// Here's where LLDestroyClass<T> specifies the subclass destroyClass() method.
|
||||
template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sRegister(&T::destroyClass);
|
||||
|
||||
#endif /* ! defined(LL_LLINITDESTROYCLASS_H) */
|
||||
|
|
@ -0,0 +1,215 @@
|
|||
/**
|
||||
* @file llpounceable.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2015-05-22
|
||||
* @brief LLPounceable is tangentially related to a future: it's a holder for
|
||||
* a value that may or may not exist yet. Unlike a future, though,
|
||||
* LLPounceable freely allows reading the held value. (If the held
|
||||
* type T does not have a distinguished "empty" value, consider using
|
||||
* LLPounceable<boost::optional<T>>.)
|
||||
*
|
||||
* LLPounceable::callWhenReady() is this template's claim to fame. It
|
||||
* allows its caller to "pounce" on the held value as soon as it
|
||||
* becomes non-empty. Call callWhenReady() with any C++ callable
|
||||
* accepting T. If the held value is already non-empty, callWhenReady()
|
||||
* will immediately call the callable with the held value. If the held
|
||||
* value is empty, though, callWhenReady() will enqueue the callable
|
||||
* for later. As soon as LLPounceable is assigned a non-empty held
|
||||
* value, it will flush the queue of deferred callables.
|
||||
*
|
||||
* Consider a global LLMessageSystem* gMessageSystem. Message system
|
||||
* initialization happens at a very specific point during viewer
|
||||
* initialization. Other subsystems want to register callbacks on the
|
||||
* LLMessageSystem instance as soon as it's initialized, but their own
|
||||
* initialization may precede that. If we define gMessageSystem to be
|
||||
* an LLPounceable<LLMessageSystem*>, a subsystem can use
|
||||
* callWhenReady() to either register immediately (if gMessageSystem
|
||||
* is already up and runnning) or register as soon as gMessageSystem
|
||||
* is set with a new, initialized instance.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
|
||||
* Copyright (c) 2015, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLPOUNCEABLE_H)
|
||||
#define LL_LLPOUNCEABLE_H
|
||||
|
||||
#include "llsingleton.h"
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/call_traits.hpp>
|
||||
#include <boost/type_traits/remove_pointer.hpp>
|
||||
#include <boost/utility/value_init.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <boost/signals2/signal.hpp>
|
||||
|
||||
// Forward declare the user template, since we want to be able to point to it
|
||||
// in some of its implementation classes.
|
||||
template <typename T, class TAG>
|
||||
class LLPounceable;
|
||||
|
||||
template <typename T, typename TAG>
|
||||
struct LLPounceableTraits
|
||||
{
|
||||
// Our "queue" is a signal object with correct signature.
|
||||
typedef boost::signals2::signal<void (typename boost::call_traits<T>::param_type)> signal_t;
|
||||
// Call callWhenReady() with any callable accepting T.
|
||||
typedef typename signal_t::slot_type func_t;
|
||||
// owner pointer type
|
||||
typedef LLPounceable<T, TAG>* owner_ptr;
|
||||
};
|
||||
|
||||
// Tag types distinguish the two different implementations of LLPounceable's
|
||||
// queue.
|
||||
struct LLPounceableQueue {};
|
||||
struct LLPounceableStatic {};
|
||||
|
||||
// generic LLPounceableQueueImpl deliberately omitted: only the above tags are
|
||||
// legal
|
||||
template <typename T, class TAG>
|
||||
class LLPounceableQueueImpl;
|
||||
|
||||
// The implementation selected by LLPounceableStatic uses an LLSingleton
|
||||
// because we can't count on a data member queue being initialized at the time
|
||||
// we start getting callWhenReady() calls. This is that LLSingleton.
|
||||
template <typename T>
|
||||
class LLPounceableQueueSingleton:
|
||||
public LLSingleton<LLPounceableQueueSingleton<T> >
|
||||
{
|
||||
private:
|
||||
typedef LLPounceableTraits<T, LLPounceableStatic> traits;
|
||||
typedef typename traits::owner_ptr owner_ptr;
|
||||
typedef typename traits::signal_t signal_t;
|
||||
|
||||
// For a given held type T, every LLPounceable<T, LLPounceableStatic>
|
||||
// instance will call on the SAME LLPounceableQueueSingleton instance --
|
||||
// given how class statics work. We must keep a separate queue for each
|
||||
// LLPounceable instance. Use a hash map for that.
|
||||
typedef boost::unordered_map<owner_ptr, signal_t> map_t;
|
||||
|
||||
public:
|
||||
// Disambiguate queues belonging to different LLPounceables.
|
||||
signal_t& get(owner_ptr owner)
|
||||
{
|
||||
// operator[] has find-or-create semantics -- just what we want!
|
||||
return mMap[owner];
|
||||
}
|
||||
|
||||
private:
|
||||
map_t mMap;
|
||||
};
|
||||
|
||||
// LLPounceableQueueImpl that uses the above LLSingleton
|
||||
template <typename T>
|
||||
class LLPounceableQueueImpl<T, LLPounceableStatic>
|
||||
{
|
||||
public:
|
||||
typedef LLPounceableTraits<T, LLPounceableStatic> traits;
|
||||
typedef typename traits::owner_ptr owner_ptr;
|
||||
typedef typename traits::signal_t signal_t;
|
||||
|
||||
signal_t& get(owner_ptr owner) const
|
||||
{
|
||||
// this Impl contains nothing; it delegates to the Singleton
|
||||
return LLPounceableQueueSingleton<T>::instance().get(owner);
|
||||
}
|
||||
};
|
||||
|
||||
// The implementation selected by LLPounceableQueue directly contains the
|
||||
// queue of interest, suitable for an LLPounceable we can trust to be fully
|
||||
// initialized when it starts getting callWhenReady() calls.
|
||||
template <typename T>
|
||||
class LLPounceableQueueImpl<T, LLPounceableQueue>
|
||||
{
|
||||
public:
|
||||
typedef LLPounceableTraits<T, LLPounceableQueue> traits;
|
||||
typedef typename traits::owner_ptr owner_ptr;
|
||||
typedef typename traits::signal_t signal_t;
|
||||
|
||||
signal_t& get(owner_ptr)
|
||||
{
|
||||
return mQueue;
|
||||
}
|
||||
|
||||
private:
|
||||
signal_t mQueue;
|
||||
};
|
||||
|
||||
// LLPounceable<T> is for an LLPounceable instance on the heap or the stack.
|
||||
// LLPounceable<T, LLPounceableStatic> is for a static LLPounceable instance.
|
||||
template <typename T, class TAG=LLPounceableQueue>
|
||||
class LLPounceable: public boost::noncopyable
|
||||
{
|
||||
private:
|
||||
typedef LLPounceableTraits<T, TAG> traits;
|
||||
typedef typename traits::owner_ptr owner_ptr;
|
||||
typedef typename traits::signal_t signal_t;
|
||||
|
||||
public:
|
||||
typedef typename traits::func_t func_t;
|
||||
|
||||
// By default, both the initial value and the distinguished empty value
|
||||
// are a default-constructed T instance. However you can explicitly
|
||||
// specify each.
|
||||
LLPounceable(typename boost::call_traits<T>::value_type init =boost::value_initialized<T>(),
|
||||
typename boost::call_traits<T>::param_type empty=boost::value_initialized<T>()):
|
||||
mHeld(init),
|
||||
mEmpty(empty)
|
||||
{}
|
||||
|
||||
// make read access to mHeld as cheap and transparent as possible
|
||||
operator T () const { return mHeld; }
|
||||
typename boost::remove_pointer<T>::type operator*() const { return *mHeld; }
|
||||
typename boost::call_traits<T>::value_type operator->() const { return mHeld; }
|
||||
// uncomment 'explicit' as soon as we allow C++11 compilation
|
||||
/*explicit*/ operator bool() const { return bool(mHeld); }
|
||||
bool operator!() const { return ! mHeld; }
|
||||
|
||||
// support both assignment (dumb ptr idiom) and reset() (smart ptr)
|
||||
void operator=(typename boost::call_traits<T>::param_type value)
|
||||
{
|
||||
reset(value);
|
||||
}
|
||||
|
||||
void reset(typename boost::call_traits<T>::param_type value)
|
||||
{
|
||||
mHeld = value;
|
||||
// If this new value is non-empty, flush anything pending in the queue.
|
||||
if (mHeld != mEmpty)
|
||||
{
|
||||
signal_t& signal(get_signal());
|
||||
signal(mHeld);
|
||||
signal.disconnect_all_slots();
|
||||
}
|
||||
}
|
||||
|
||||
// our claim to fame
|
||||
void callWhenReady(const func_t& func)
|
||||
{
|
||||
if (mHeld != mEmpty)
|
||||
{
|
||||
// If the held value is already non-empty, immediately call func()
|
||||
func(mHeld);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Held value still empty, queue func() for later. By default,
|
||||
// connect() enqueues slots in FIFO order.
|
||||
get_signal().connect(func);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
signal_t& get_signal() { return mQueue.get(this); }
|
||||
|
||||
// Store both the current and the empty value.
|
||||
// MAYBE: Might be useful to delegate to LLPounceableTraits the meaning of
|
||||
// testing for "empty." For some types we want operator!(); for others we
|
||||
// want to compare to a distinguished value.
|
||||
typename boost::call_traits<T>::value_type mHeld, mEmpty;
|
||||
// This might either contain the queue (LLPounceableQueue) or delegate to
|
||||
// an LLSingleton (LLPounceableStatic).
|
||||
LLPounceableQueueImpl<T, TAG> mQueue;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLPOUNCEABLE_H) */
|
||||
|
|
@ -269,7 +269,7 @@ public:
|
|||
|
||||
~ScopedRegistrar()
|
||||
{
|
||||
if (!singleton_t::destroyed())
|
||||
if (singleton_t::instanceExists())
|
||||
{
|
||||
popScope();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,332 @@
|
|||
*/
|
||||
|
||||
#include "linden_common.h"
|
||||
|
||||
#include "llsingleton.h"
|
||||
|
||||
#include "llerror.h"
|
||||
#include "llerrorcontrol.h" // LLError::is_available()
|
||||
#include "lldependencies.h"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <algorithm>
|
||||
#include <iostream> // std::cerr in dire emergency
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
// Our master list of all LLSingletons is itself an LLSingleton. We used to
|
||||
// store it in a function-local static, but that could get destroyed before
|
||||
// the last of the LLSingletons -- and ~LLSingletonBase() definitely wants to
|
||||
// remove itself from the master list. Since the whole point of this master
|
||||
// list is to help track inter-LLSingleton dependencies, and since we have
|
||||
// this implicit dependency from every LLSingleton to the master list, make it
|
||||
// an LLSingleton.
|
||||
class LLSingletonBase::MasterList:
|
||||
public LLSingleton<LLSingletonBase::MasterList>
|
||||
{
|
||||
private:
|
||||
friend class LLSingleton<LLSingletonBase::MasterList>;
|
||||
|
||||
public:
|
||||
// No need to make this private with accessors; nobody outside this source
|
||||
// file can see it.
|
||||
LLSingletonBase::list_t mList;
|
||||
};
|
||||
|
||||
//static
|
||||
LLSingletonBase::list_t& LLSingletonBase::get_master()
|
||||
{
|
||||
return LLSingletonBase::MasterList::instance().mList;
|
||||
}
|
||||
|
||||
void LLSingletonBase::add_master()
|
||||
{
|
||||
// As each new LLSingleton is constructed, add to the master list.
|
||||
get_master().push_back(this);
|
||||
}
|
||||
|
||||
void LLSingletonBase::remove_master()
|
||||
{
|
||||
// When an LLSingleton is destroyed, remove from master list.
|
||||
// add_master() used to capture the iterator to the newly-added list item
|
||||
// so we could directly erase() it from the master list. Unfortunately
|
||||
// that runs afoul of destruction-dependency order problems. So search the
|
||||
// master list, and remove this item IF FOUND. We have few enough
|
||||
// LLSingletons, and they are so rarely destroyed (once per run), that the
|
||||
// cost of a linear search should not be an issue.
|
||||
get_master().remove(this);
|
||||
}
|
||||
|
||||
// Wrapping our initializing list in a static method ensures that it will be
|
||||
// constructed on demand. This list doesn't also need to be in an LLSingleton
|
||||
// because (a) it should be empty by program shutdown and (b) none of our
|
||||
// destructors reference it.
|
||||
//static
|
||||
LLSingletonBase::list_t& LLSingletonBase::get_initializing()
|
||||
{
|
||||
static list_t sList;
|
||||
return sList;
|
||||
}
|
||||
|
||||
LLSingletonBase::LLSingletonBase():
|
||||
mCleaned(false),
|
||||
mDeleteSingleton(NULL)
|
||||
{
|
||||
// Make this the currently-initializing LLSingleton.
|
||||
push_initializing();
|
||||
}
|
||||
|
||||
LLSingletonBase::~LLSingletonBase() {}
|
||||
|
||||
void LLSingletonBase::push_initializing()
|
||||
{
|
||||
get_initializing().push_back(this);
|
||||
}
|
||||
|
||||
void LLSingletonBase::pop_initializing()
|
||||
{
|
||||
list_t& list(get_initializing());
|
||||
if (list.empty())
|
||||
{
|
||||
logerrs("Underflow in stack of currently-initializing LLSingletons at ",
|
||||
demangle(typeid(*this).name()).c_str(), "::getInstance()");
|
||||
}
|
||||
if (list.back() != this)
|
||||
{
|
||||
logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ",
|
||||
demangle(typeid(*this).name()).c_str(), "::getInstance() trying to pop ",
|
||||
demangle(typeid(*list.back()).name()).c_str());
|
||||
}
|
||||
// Here we're sure that list.back() == this. Whew, pop it.
|
||||
list.pop_back();
|
||||
}
|
||||
|
||||
void LLSingletonBase::capture_dependency(EInitState initState)
|
||||
{
|
||||
// Did this getInstance() call come from another LLSingleton, or from
|
||||
// vanilla application code? Note that although this is a nontrivial
|
||||
// method, the vast majority of its calls arrive here with initializing
|
||||
// empty().
|
||||
list_t& initializing(get_initializing());
|
||||
if (! initializing.empty())
|
||||
{
|
||||
// getInstance() is being called by some other LLSingleton. But -- is
|
||||
// this a circularity? That is, does 'this' already appear in the
|
||||
// initializing stack?
|
||||
// For what it's worth, normally 'initializing' should contain very
|
||||
// few elements.
|
||||
list_t::const_iterator found =
|
||||
std::find(initializing.begin(), initializing.end(), this);
|
||||
if (found != initializing.end())
|
||||
{
|
||||
// Report the circularity. Requiring the coder to dig through the
|
||||
// logic to diagnose exactly how we got here is less than helpful.
|
||||
std::ostringstream out;
|
||||
for ( ; found != initializing.end(); ++found)
|
||||
{
|
||||
// 'found' is an iterator; *found is an LLSingletonBase*; **found
|
||||
// is the actual LLSingletonBase instance.
|
||||
out << demangle(typeid(**found).name()) << " -> ";
|
||||
}
|
||||
// We promise to capture dependencies from both the constructor
|
||||
// and the initSingleton() method, so an LLSingleton's instance
|
||||
// pointer is on the initializing list during both. Now that we've
|
||||
// detected circularity, though, we must distinguish the two. If
|
||||
// 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(),
|
||||
demangle(typeid(*this).name()).c_str(), "");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Here 'this' is NOT already in the 'initializing' stack. Great!
|
||||
// Record the dependency.
|
||||
// initializing.back() is the LLSingletonBase* currently being
|
||||
// initialized. Store 'this' in its mDepends set.
|
||||
initializing.back()->mDepends.insert(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
LLSingletonBase::vec_t LLSingletonBase::dep_sort()
|
||||
{
|
||||
// While it would theoretically be possible to maintain a static
|
||||
// SingletonDeps through the life of the program, dynamically adding and
|
||||
// removing LLSingletons as they are created and destroyed, in practice
|
||||
// it's less messy to construct it on demand. The overhead of doing so
|
||||
// should happen basically twice: once for cleanupAll(), once for
|
||||
// deleteAll().
|
||||
typedef LLDependencies<LLSingletonBase*> SingletonDeps;
|
||||
SingletonDeps sdeps;
|
||||
list_t& master(get_master());
|
||||
BOOST_FOREACH(LLSingletonBase* sp, master)
|
||||
{
|
||||
// Build the SingletonDeps structure by adding, for each
|
||||
// LLSingletonBase* sp in the master list, sp itself. It has no
|
||||
// associated value type in our SingletonDeps, hence the 0. We don't
|
||||
// record the LLSingletons it must follow; rather, we record the ones
|
||||
// it must precede. Copy its mDepends to a KeyList to express that.
|
||||
sdeps.add(sp, 0,
|
||||
SingletonDeps::KeyList(),
|
||||
SingletonDeps::KeyList(sp->mDepends.begin(), sp->mDepends.end()));
|
||||
}
|
||||
vec_t ret;
|
||||
ret.reserve(master.size());
|
||||
// We should be able to effect this with a transform_iterator that
|
||||
// extracts just the first (key) element from each sorted_iterator, then
|
||||
// uses vec_t's range constructor... but frankly this is more
|
||||
// straightforward, as long as we remember the above reserve() call!
|
||||
BOOST_FOREACH(SingletonDeps::sorted_iterator::value_type pair, sdeps.sort())
|
||||
{
|
||||
ret.push_back(pair.first);
|
||||
}
|
||||
// The master list is not itself pushed onto the master list. Add it as
|
||||
// the very last entry -- it is the LLSingleton on which ALL others
|
||||
// depend! -- so our caller will process it.
|
||||
ret.push_back(MasterList::getInstance());
|
||||
return ret;
|
||||
}
|
||||
|
||||
//static
|
||||
void LLSingletonBase::cleanupAll()
|
||||
{
|
||||
// It's essential to traverse these in dependency order.
|
||||
BOOST_FOREACH(LLSingletonBase* sp, dep_sort())
|
||||
{
|
||||
// Call cleanupSingleton() only if we haven't already done so for this
|
||||
// instance.
|
||||
if (! sp->mCleaned)
|
||||
{
|
||||
sp->mCleaned = true;
|
||||
|
||||
try
|
||||
{
|
||||
sp->cleanupSingleton();
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
logwarns("Exception in ", demangle(typeid(*sp).name()).c_str(),
|
||||
"::cleanupSingleton(): ", e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logwarns("Unknown exception in ", demangle(typeid(*sp).name()).c_str(),
|
||||
"::cleanupSingleton()");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLSingletonBase::deleteAll()
|
||||
{
|
||||
// It's essential to traverse these in dependency order.
|
||||
BOOST_FOREACH(LLSingletonBase* sp, dep_sort())
|
||||
{
|
||||
// Capture the class name first: in case of exception, don't count on
|
||||
// being able to extract it later.
|
||||
const std::string name = demangle(typeid(*sp).name());
|
||||
try
|
||||
{
|
||||
// Call static method through instance function pointer.
|
||||
if (! sp->mDeleteSingleton)
|
||||
{
|
||||
// This Should Not Happen... but carry on.
|
||||
logwarns(name.c_str(), "::mDeleteSingleton not initialized!");
|
||||
}
|
||||
else
|
||||
{
|
||||
// properly initialized: call it.
|
||||
// From this point on, DO NOT DEREFERENCE sp!
|
||||
sp->mDeleteSingleton();
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
logwarns("Exception in ", name.c_str(), "::deleteSingleton(): ", e.what());
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
logwarns("Unknown exception in ", name.c_str(), "::deleteSingleton()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*------------------------ Final cleanup management ------------------------*/
|
||||
class LLSingletonBase::MasterRefcount
|
||||
{
|
||||
public:
|
||||
// store a POD int so it will be statically initialized to 0
|
||||
int refcount;
|
||||
};
|
||||
static LLSingletonBase::MasterRefcount sMasterRefcount;
|
||||
|
||||
LLSingletonBase::ref_ptr_t LLSingletonBase::get_master_refcount()
|
||||
{
|
||||
// Calling this method constructs a new ref_ptr_t, which implicitly calls
|
||||
// intrusive_ptr_add_ref(MasterRefcount*).
|
||||
return &sMasterRefcount;
|
||||
}
|
||||
|
||||
void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount* mrc)
|
||||
{
|
||||
// Count outstanding SingletonLifetimeManager instances.
|
||||
++mrc->refcount;
|
||||
}
|
||||
|
||||
void intrusive_ptr_release(LLSingletonBase::MasterRefcount* mrc)
|
||||
{
|
||||
// Notice when each SingletonLifetimeManager instance is destroyed.
|
||||
if (! --mrc->refcount)
|
||||
{
|
||||
// The last instance was destroyed. Time to kill any remaining
|
||||
// LLSingletons -- but in dependency order.
|
||||
LLSingletonBase::deleteAll();
|
||||
}
|
||||
}
|
||||
|
||||
/*---------------------------- Logging helpers -----------------------------*/
|
||||
//static
|
||||
void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4)
|
||||
{
|
||||
// Check LLError::is_available() because some of LLError's infrastructure
|
||||
// is itself an LLSingleton. If that LLSingleton has not yet been
|
||||
// initialized, trying to log will engage LLSingleton machinery... and
|
||||
// around and around we go.
|
||||
if (LLError::is_available())
|
||||
{
|
||||
LL_ERRS() << p1 << p2 << p3 << p4 << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Caller may be a test program, or something else whose stderr is
|
||||
// visible to the user.
|
||||
std::cerr << p1 << p2 << p3 << p4 << std::endl;
|
||||
// The other important side effect of LL_ERRS() is
|
||||
// https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG)
|
||||
LLError::crashAndLoop(std::string());
|
||||
}
|
||||
}
|
||||
|
||||
//static
|
||||
void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4)
|
||||
{
|
||||
// See logerrs() remarks about is_available().
|
||||
if (LLError::is_available())
|
||||
{
|
||||
LL_WARNS() << p1 << p2 << p3 << p4 << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << p1 << p2 << p3 << p4 << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::string LLSingletonBase::demangle(const char* mangled)
|
||||
{
|
||||
return LLError::Log::demangle(mangled);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,185 +25,387 @@
|
|||
#ifndef LLSINGLETON_H
|
||||
#define LLSINGLETON_H
|
||||
|
||||
#include "llerror.h" // *TODO: eliminate this
|
||||
|
||||
#include <typeinfo>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <boost/unordered_set.hpp>
|
||||
#include <boost/intrusive_ptr.hpp>
|
||||
#include <list>
|
||||
#include <vector>
|
||||
#include <typeinfo>
|
||||
|
||||
// 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.
|
||||
//
|
||||
// There are two ways to use LLSingleton. The first way is to inherit from it
|
||||
// while using the typename that you'd like to be static as the template
|
||||
// parameter, like so:
|
||||
//
|
||||
// class Foo: public LLSingleton<Foo>{};
|
||||
//
|
||||
// Foo& instance = Foo::instance();
|
||||
//
|
||||
// The second way is to use the singleton class directly, without inheritance:
|
||||
//
|
||||
// typedef LLSingleton<Foo> FooSingleton;
|
||||
//
|
||||
// Foo& instance = FooSingleton::instance();
|
||||
//
|
||||
// In this case, the class being managed as a singleton needs to provide an
|
||||
// initSingleton() method since the LLSingleton virtual method won't be
|
||||
// available
|
||||
//
|
||||
// As currently written, it is not thread-safe.
|
||||
|
||||
template <typename DERIVED_TYPE>
|
||||
class LLSingleton : private boost::noncopyable
|
||||
class LLSingletonBase: private boost::noncopyable
|
||||
{
|
||||
public:
|
||||
class MasterList;
|
||||
class MasterRefcount;
|
||||
typedef boost::intrusive_ptr<MasterRefcount> ref_ptr_t;
|
||||
|
||||
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();
|
||||
// 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;
|
||||
|
||||
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
|
||||
// constructor.
|
||||
LLSingletonBase();
|
||||
virtual ~LLSingletonBase();
|
||||
|
||||
// Every new LLSingleton should be added to/removed from the master list
|
||||
void add_master();
|
||||
void remove_master();
|
||||
// with a little help from our friends.
|
||||
template <class T> friend struct LLSingleton_manage_master;
|
||||
|
||||
// Maintain a stack of the LLSingleton subclass instance currently being
|
||||
// initialized. We use this to notice direct dependencies: we want to know
|
||||
// if A requires B. We deduce a dependency if while initializing A,
|
||||
// control reaches B::getInstance().
|
||||
// We want &A to be at the top of that stack during both A::A() and
|
||||
// A::initSingleton(), since a call to B::getInstance() might occur during
|
||||
// either.
|
||||
// Unfortunately the desired timespan does not correspond neatly with a
|
||||
// single C++ scope, else we'd use RAII to track it. But we do know that
|
||||
// LLSingletonBase's constructor definitely runs just before
|
||||
// LLSingleton's, which runs just before the specific subclass's.
|
||||
void push_initializing();
|
||||
// LLSingleton is, and must remain, the only caller to initSingleton().
|
||||
// That being the case, we control exactly when it happens -- and we can
|
||||
// pop the stack immediately thereafter.
|
||||
void pop_initializing();
|
||||
// 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(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="");
|
||||
static std::string demangle(const char* mangled);
|
||||
|
||||
// obtain canonical ref_ptr_t
|
||||
static ref_ptr_t get_master_refcount();
|
||||
|
||||
// Default methods in case subclass doesn't declare them.
|
||||
virtual void initSingleton() {}
|
||||
virtual void cleanupSingleton() {}
|
||||
|
||||
// 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
|
||||
// deleteSingleton()) store a pointer here. Since we know it's a static
|
||||
// class method, a classic-C function pointer will do.
|
||||
void (*mDeleteSingleton)();
|
||||
|
||||
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.
|
||||
*
|
||||
* The most important property of deleteAll() is that deleteSingleton()
|
||||
* 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 deleteSingleton() method throws an exception, the exception is
|
||||
* logged, but deleteAll() attempts to continue calling the rest of the
|
||||
* deleteSingleton() methods.
|
||||
*/
|
||||
static void deleteAll();
|
||||
};
|
||||
|
||||
// support ref_ptr_t
|
||||
void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount*);
|
||||
void intrusive_ptr_release(LLSingletonBase::MasterRefcount*);
|
||||
|
||||
// Most of the time, we want LLSingleton_manage_master() to forward its
|
||||
// methods to LLSingletonBase::add_master() and remove_master().
|
||||
template <class T>
|
||||
struct LLSingleton_manage_master
|
||||
{
|
||||
void add(LLSingletonBase* sb) { sb->add_master(); }
|
||||
void remove(LLSingletonBase* sb) { sb->remove_master(); }
|
||||
};
|
||||
|
||||
// But for the specific case of LLSingletonBase::MasterList, don't.
|
||||
template <>
|
||||
struct LLSingleton_manage_master<LLSingletonBase::MasterList>
|
||||
{
|
||||
void add(LLSingletonBase*) {}
|
||||
void remove(LLSingletonBase*) {}
|
||||
};
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Derive your class from LLSingleton, passing your subclass name as
|
||||
* LLSingleton's template parameter, like so:
|
||||
*
|
||||
* class Foo: public LLSingleton<Foo>{};
|
||||
*
|
||||
* Foo& instance = Foo::instance();
|
||||
*
|
||||
* LLSingleton recognizes a couple special methods in your derived class.
|
||||
*
|
||||
* If you override LLSingleton<T>::initSingleton(), your method will be called
|
||||
* immediately after the instance is constructed. This is useful for breaking
|
||||
* circular dependencies: if you find that your LLSingleton subclass
|
||||
* constructor references other LLSingleton subclass instances in a chain
|
||||
* 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.
|
||||
*
|
||||
* 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
|
||||
* B::instance() or B::getInstance(), and C::instance() or C::getInstance().
|
||||
* 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().
|
||||
* 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:
|
||||
typedef enum e_init_state
|
||||
{
|
||||
UNINITIALIZED,
|
||||
CONSTRUCTING,
|
||||
INITIALIZING,
|
||||
INITIALIZED,
|
||||
DELETED
|
||||
} EInitState;
|
||||
|
||||
static DERIVED_TYPE* constructSingleton()
|
||||
{
|
||||
return new DERIVED_TYPE();
|
||||
}
|
||||
|
||||
// stores pointer to singleton instance
|
||||
struct SingletonLifetimeManager
|
||||
{
|
||||
SingletonLifetimeManager()
|
||||
{
|
||||
construct();
|
||||
}
|
||||
|
||||
static void construct()
|
||||
{
|
||||
sData.mInitState = CONSTRUCTING;
|
||||
sData.mInstance = constructSingleton();
|
||||
sData.mInitState = INITIALIZING;
|
||||
}
|
||||
// stores pointer to singleton instance
|
||||
struct SingletonLifetimeManager
|
||||
{
|
||||
SingletonLifetimeManager():
|
||||
mMasterRefcount(LLSingletonBase::get_master_refcount())
|
||||
{
|
||||
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. What we really want is to count the
|
||||
// runtime's attempts to cleanup LLSingleton static data -- and on
|
||||
// the very last one, call LLSingletonBase::deleteAll(). That
|
||||
// method will properly honor cross-LLSingleton dependencies. This
|
||||
// is why we store an intrusive_ptr to a MasterRefcount: our
|
||||
// ref_ptr_t member counts SingletonLifetimeManager instances.
|
||||
// Once the runtime destroys the last of these, THEN we can delete
|
||||
// every remaining LLSingleton.
|
||||
}
|
||||
|
||||
LLSingletonBase::ref_ptr_t mMasterRefcount;
|
||||
};
|
||||
|
||||
protected:
|
||||
LLSingleton()
|
||||
{
|
||||
// populate base-class function pointer with the static
|
||||
// deleteSingleton() function for this particular specialization
|
||||
mDeleteSingleton = &deleteSingleton;
|
||||
|
||||
// add this new instance to the master list
|
||||
LLSingleton_manage_master<DERIVED_TYPE>().add(this);
|
||||
}
|
||||
|
||||
~SingletonLifetimeManager()
|
||||
{
|
||||
if (sData.mInitState != DELETED)
|
||||
{
|
||||
deleteSingleton();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
virtual ~LLSingleton()
|
||||
{
|
||||
sData.mInstance = NULL;
|
||||
sData.mInitState = DELETED;
|
||||
}
|
||||
virtual ~LLSingleton()
|
||||
{
|
||||
// remove this instance from the master list
|
||||
LLSingleton_manage_master<DERIVED_TYPE>().remove(this);
|
||||
sData.mInstance = NULL;
|
||||
sData.mInitState = DELETED;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Immediately delete the singleton.
|
||||
*
|
||||
* A subsequent call to LLProxy::getInstance() will construct a new
|
||||
* instance of the class.
|
||||
*
|
||||
* LLSingletons are normally 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static void deleteSingleton()
|
||||
{
|
||||
delete sData.mInstance;
|
||||
sData.mInstance = NULL;
|
||||
sData.mInitState = DELETED;
|
||||
}
|
||||
/**
|
||||
* @brief Immediately delete the singleton.
|
||||
*
|
||||
* A subsequent call to LLProxy::getInstance() will construct a new
|
||||
* instance of the class.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
static void deleteSingleton()
|
||||
{
|
||||
delete sData.mInstance;
|
||||
sData.mInstance = NULL;
|
||||
sData.mInitState = DELETED;
|
||||
}
|
||||
|
||||
static DERIVED_TYPE* getInstance()
|
||||
{
|
||||
static SingletonLifetimeManager sLifeTimeMgr;
|
||||
|
||||
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;
|
||||
|
||||
switch (sData.mInitState)
|
||||
{
|
||||
case UNINITIALIZED:
|
||||
// should never be uninitialized at this point
|
||||
llassert(false);
|
||||
return NULL;
|
||||
case CONSTRUCTING:
|
||||
LL_ERRS() << "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:
|
||||
LL_WARNS() << "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;
|
||||
}
|
||||
case CONSTRUCTING:
|
||||
logerrs("Tried to access singleton ",
|
||||
demangle(typeid(DERIVED_TYPE).name()).c_str(),
|
||||
" from singleton constructor!");
|
||||
return NULL;
|
||||
|
||||
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
|
||||
sData.mInstance->pop_initializing();
|
||||
break;
|
||||
|
||||
static DERIVED_TYPE* getIfExists()
|
||||
{
|
||||
return sData.mInstance;
|
||||
}
|
||||
case INITIALIZED:
|
||||
break;
|
||||
|
||||
// Reference version of getInstance()
|
||||
// Preferred over getInstance() as it disallows checking for NULL
|
||||
static DERIVED_TYPE& instance()
|
||||
{
|
||||
return *getInstance();
|
||||
}
|
||||
|
||||
// Has this singleton been created uet?
|
||||
// Use this to avoid accessing singletons before the can safely be constructed
|
||||
static bool instanceExists()
|
||||
{
|
||||
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 sData.mInitState == DELETED;
|
||||
}
|
||||
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
|
||||
sData.mInstance->pop_initializing();
|
||||
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(sData.mInitState);
|
||||
return sData.mInstance;
|
||||
}
|
||||
|
||||
// Reference version of getInstance()
|
||||
// Preferred over getInstance() as it disallows checking for NULL
|
||||
static DERIVED_TYPE& instance()
|
||||
{
|
||||
return *getInstance();
|
||||
}
|
||||
|
||||
// Has this singleton been created yet?
|
||||
// Use this to avoid accessing singletons before they can safely be constructed.
|
||||
static bool instanceExists()
|
||||
{
|
||||
return sData.mInitState == INITIALIZED;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
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;
|
||||
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;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,230 @@
|
|||
/**
|
||||
* @file llpounceable_test.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2015-05-22
|
||||
* @brief Test for llpounceable.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2015&license=viewerlgpl$
|
||||
* Copyright (c) 2015, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Precompiled header
|
||||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "llpounceable.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
#include <boost/bind.hpp>
|
||||
// other Linden headers
|
||||
#include "../test/lltut.h"
|
||||
|
||||
/*----------------------------- string testing -----------------------------*/
|
||||
void append(std::string* dest, const std::string& src)
|
||||
{
|
||||
dest->append(src);
|
||||
}
|
||||
|
||||
/*-------------------------- Data-struct testing ---------------------------*/
|
||||
struct Data
|
||||
{
|
||||
Data(const std::string& data):
|
||||
mData(data)
|
||||
{}
|
||||
const std::string mData;
|
||||
};
|
||||
|
||||
void setter(Data** dest, Data* ptr)
|
||||
{
|
||||
*dest = ptr;
|
||||
}
|
||||
|
||||
static Data* static_check = 0;
|
||||
|
||||
// Set up an extern pointer to an LLPounceableStatic so the linker will fill
|
||||
// in the forward reference from below, before runtime.
|
||||
extern LLPounceable<Data*, LLPounceableStatic> gForward;
|
||||
|
||||
struct EnqueueCall
|
||||
{
|
||||
EnqueueCall()
|
||||
{
|
||||
// Intentionally use a forward reference to an LLPounceableStatic that
|
||||
// we believe is NOT YET CONSTRUCTED. This models the scenario in
|
||||
// which a constructor in another translation unit runs before
|
||||
// constructors in this one. We very specifically want callWhenReady()
|
||||
// to work even in that case: we need the LLPounceableQueueImpl to be
|
||||
// initialized even if the LLPounceable itself is not.
|
||||
gForward.callWhenReady(boost::bind(setter, &static_check, _1));
|
||||
}
|
||||
} nqcall;
|
||||
// When this declaration is processed, we should enqueue the
|
||||
// setter(&static_check, _1) call for when gForward is set non-NULL. Needless
|
||||
// to remark, we want this call not to crash.
|
||||
|
||||
// Now declare gForward. Its constructor should not run until after nqcall's.
|
||||
LLPounceable<Data*, LLPounceableStatic> gForward;
|
||||
|
||||
/*****************************************************************************
|
||||
* TUT
|
||||
*****************************************************************************/
|
||||
namespace tut
|
||||
{
|
||||
struct llpounceable_data
|
||||
{
|
||||
};
|
||||
typedef test_group<llpounceable_data> llpounceable_group;
|
||||
typedef llpounceable_group::object object;
|
||||
llpounceable_group llpounceablegrp("llpounceable");
|
||||
|
||||
template<> template<>
|
||||
void object::test<1>()
|
||||
{
|
||||
set_test_name("LLPounceableStatic out-of-order test");
|
||||
// LLPounceable<T, LLPounceableStatic>::callWhenReady() must work even
|
||||
// before LLPounceable's constructor runs. That's the whole point of
|
||||
// implementing it with an LLSingleton queue. This models (say)
|
||||
// LLPounceableStatic<LLMessageSystem*, LLPounceableStatic>.
|
||||
ensure("static_check should still be null", ! static_check);
|
||||
Data myData("test<1>");
|
||||
gForward = &myData; // should run setter
|
||||
ensure_equals("static_check should be &myData", static_check, &myData);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void object::test<2>()
|
||||
{
|
||||
set_test_name("LLPounceableQueue different queues");
|
||||
// We expect that LLPounceable<T, LLPounceableQueue> should have
|
||||
// different queues because that specialization stores the queue
|
||||
// directly in the LLPounceable instance.
|
||||
Data *aptr = 0, *bptr = 0;
|
||||
LLPounceable<Data*> a, b;
|
||||
a.callWhenReady(boost::bind(setter, &aptr, _1));
|
||||
b.callWhenReady(boost::bind(setter, &bptr, _1));
|
||||
ensure("aptr should be null", ! aptr);
|
||||
ensure("bptr should be null", ! bptr);
|
||||
Data adata("a"), bdata("b");
|
||||
a = &adata;
|
||||
ensure_equals("aptr should be &adata", aptr, &adata);
|
||||
// but we haven't yet set b
|
||||
ensure("bptr should still be null", !bptr);
|
||||
b = &bdata;
|
||||
ensure_equals("bptr should be &bdata", bptr, &bdata);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void object::test<3>()
|
||||
{
|
||||
set_test_name("LLPounceableStatic different queues");
|
||||
// LLPounceable<T, LLPounceableStatic> should also have a distinct
|
||||
// queue for each instance, but that engages an additional map lookup
|
||||
// because there's only one LLSingleton for each T.
|
||||
Data *aptr = 0, *bptr = 0;
|
||||
LLPounceable<Data*, LLPounceableStatic> a, b;
|
||||
a.callWhenReady(boost::bind(setter, &aptr, _1));
|
||||
b.callWhenReady(boost::bind(setter, &bptr, _1));
|
||||
ensure("aptr should be null", ! aptr);
|
||||
ensure("bptr should be null", ! bptr);
|
||||
Data adata("a"), bdata("b");
|
||||
a = &adata;
|
||||
ensure_equals("aptr should be &adata", aptr, &adata);
|
||||
// but we haven't yet set b
|
||||
ensure("bptr should still be null", !bptr);
|
||||
b = &bdata;
|
||||
ensure_equals("bptr should be &bdata", bptr, &bdata);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void object::test<4>()
|
||||
{
|
||||
set_test_name("LLPounceable<T> looks like T");
|
||||
// We want LLPounceable<T, TAG> to be drop-in replaceable for a plain
|
||||
// T for read constructs. In particular, it should behave like a dumb
|
||||
// pointer -- and with zero abstraction cost for such usage.
|
||||
Data* aptr = 0;
|
||||
Data a("a");
|
||||
// should be able to initialize a pounceable (when its constructor
|
||||
// runs)
|
||||
LLPounceable<Data*> pounceable(&a);
|
||||
// should be able to pass LLPounceable<T> to function accepting T
|
||||
setter(&aptr, pounceable);
|
||||
ensure_equals("aptr should be &a", aptr, &a);
|
||||
// should be able to dereference with *
|
||||
ensure_equals("deref with *", (*pounceable).mData, "a");
|
||||
// should be able to dereference with ->
|
||||
ensure_equals("deref with ->", pounceable->mData, "a");
|
||||
// bool operations
|
||||
ensure("test with operator bool()", pounceable);
|
||||
ensure("test with operator !()", ! (! pounceable));
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void object::test<5>()
|
||||
{
|
||||
set_test_name("Multiple callWhenReady() queue items");
|
||||
Data *p1 = 0, *p2 = 0, *p3 = 0;
|
||||
Data a("a");
|
||||
LLPounceable<Data*> pounceable;
|
||||
// queue up a couple setter() calls for later
|
||||
pounceable.callWhenReady(boost::bind(setter, &p1, _1));
|
||||
pounceable.callWhenReady(boost::bind(setter, &p2, _1));
|
||||
// should still be pending
|
||||
ensure("p1 should be null", !p1);
|
||||
ensure("p2 should be null", !p2);
|
||||
ensure("p3 should be null", !p3);
|
||||
pounceable = 0;
|
||||
// assigning a new empty value shouldn't flush the queue
|
||||
ensure("p1 should still be null", !p1);
|
||||
ensure("p2 should still be null", !p2);
|
||||
ensure("p3 should still be null", !p3);
|
||||
// using whichever syntax
|
||||
pounceable.reset(0);
|
||||
// try to make ensure messages distinct... tough to pin down which
|
||||
// ensure() failed if multiple ensure() calls in the same test<n> have
|
||||
// the same message!
|
||||
ensure("p1 should again be null", !p1);
|
||||
ensure("p2 should again be null", !p2);
|
||||
ensure("p3 should again be null", !p3);
|
||||
pounceable.reset(&a); // should flush queue
|
||||
ensure_equals("p1 should be &a", p1, &a);
|
||||
ensure_equals("p2 should be &a", p2, &a);
|
||||
ensure("p3 still not set", !p3);
|
||||
// immediate call
|
||||
pounceable.callWhenReady(boost::bind(setter, &p3, _1));
|
||||
ensure_equals("p3 should be &a", p3, &a);
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void object::test<6>()
|
||||
{
|
||||
set_test_name("queue order");
|
||||
std::string data;
|
||||
LLPounceable<std::string*> pounceable;
|
||||
pounceable.callWhenReady(boost::bind(append, _1, "a"));
|
||||
pounceable.callWhenReady(boost::bind(append, _1, "b"));
|
||||
pounceable.callWhenReady(boost::bind(append, _1, "c"));
|
||||
pounceable = &data;
|
||||
ensure_equals("callWhenReady() must preserve chronological order",
|
||||
data, "abc");
|
||||
|
||||
std::string data2;
|
||||
pounceable = NULL;
|
||||
pounceable.callWhenReady(boost::bind(append, _1, "d"));
|
||||
pounceable.callWhenReady(boost::bind(append, _1, "e"));
|
||||
pounceable.callWhenReady(boost::bind(append, _1, "f"));
|
||||
pounceable = &data2;
|
||||
ensure_equals("LLPounceable must reset queue when fired",
|
||||
data2, "def");
|
||||
}
|
||||
|
||||
template<> template<>
|
||||
void object::test<7>()
|
||||
{
|
||||
set_test_name("compile-fail test, uncomment to check");
|
||||
// The following declaration should fail: only LLPounceableQueue and
|
||||
// LLPounceableStatic should work as tags.
|
||||
// LLPounceable<Data*, int> pounceable;
|
||||
}
|
||||
} // namespace tut
|
||||
|
|
@ -30,47 +30,171 @@
|
|||
#include "llsingleton.h"
|
||||
#include "../test/lltut.h"
|
||||
|
||||
|
||||
// Capture execution sequence by appending to log string.
|
||||
std::string sLog;
|
||||
|
||||
#define DECLARE_CLASS(CLS) \
|
||||
struct CLS: public LLSingleton<CLS> \
|
||||
{ \
|
||||
static enum dep_flag { \
|
||||
DEP_NONE, /* no dependency */ \
|
||||
DEP_CTOR, /* dependency in ctor */ \
|
||||
DEP_INIT /* dependency in initSingleton */ \
|
||||
} sDepFlag; \
|
||||
\
|
||||
CLS(); \
|
||||
void initSingleton(); \
|
||||
void cleanupSingleton(); \
|
||||
~CLS(); \
|
||||
}; \
|
||||
\
|
||||
CLS::dep_flag CLS::sDepFlag = DEP_NONE
|
||||
|
||||
DECLARE_CLASS(A);
|
||||
DECLARE_CLASS(B);
|
||||
|
||||
#define DEFINE_MEMBERS(CLS, OTHER) \
|
||||
CLS::CLS() \
|
||||
{ \
|
||||
sLog.append(#CLS); \
|
||||
if (sDepFlag == DEP_CTOR) \
|
||||
{ \
|
||||
(void)OTHER::instance(); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void CLS::initSingleton() \
|
||||
{ \
|
||||
sLog.append("i" #CLS); \
|
||||
if (sDepFlag == DEP_INIT) \
|
||||
{ \
|
||||
(void)OTHER::instance(); \
|
||||
} \
|
||||
} \
|
||||
\
|
||||
void CLS::cleanupSingleton() \
|
||||
{ \
|
||||
sLog.append("x" #CLS); \
|
||||
} \
|
||||
\
|
||||
CLS::~CLS() \
|
||||
{ \
|
||||
sLog.append("~" #CLS); \
|
||||
}
|
||||
|
||||
DEFINE_MEMBERS(A, B)
|
||||
DEFINE_MEMBERS(B, A)
|
||||
|
||||
namespace tut
|
||||
{
|
||||
struct singleton
|
||||
{
|
||||
// We need a class created with the LLSingleton template to test with.
|
||||
class LLSingletonTest: public LLSingleton<LLSingletonTest>
|
||||
{
|
||||
struct singleton
|
||||
{
|
||||
// We need a class created with the LLSingleton template to test with.
|
||||
class LLSingletonTest: public LLSingleton<LLSingletonTest>
|
||||
{
|
||||
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
typedef test_group<singleton> singleton_t;
|
||||
typedef singleton_t::object singleton_object_t;
|
||||
tut::singleton_t tut_singleton("LLSingleton");
|
||||
typedef test_group<singleton> singleton_t;
|
||||
typedef singleton_t::object singleton_object_t;
|
||||
tut::singleton_t tut_singleton("LLSingleton");
|
||||
|
||||
template<> template<>
|
||||
void singleton_object_t::test<1>()
|
||||
{
|
||||
template<> template<>
|
||||
void singleton_object_t::test<1>()
|
||||
{
|
||||
|
||||
}
|
||||
template<> template<>
|
||||
void singleton_object_t::test<2>()
|
||||
{
|
||||
LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
|
||||
ensure(singleton_test);
|
||||
}
|
||||
template<> template<>
|
||||
void singleton_object_t::test<3>()
|
||||
{
|
||||
//Construct the instance
|
||||
LLSingletonTest::getInstance();
|
||||
ensure(LLSingletonTest::instanceExists());
|
||||
}
|
||||
template<> template<>
|
||||
void singleton_object_t::test<2>()
|
||||
{
|
||||
LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
|
||||
ensure(singleton_test);
|
||||
}
|
||||
|
||||
//Delete the instance
|
||||
LLSingletonTest::deleteSingleton();
|
||||
ensure(LLSingletonTest::destroyed());
|
||||
ensure(!LLSingletonTest::instanceExists());
|
||||
template<> template<>
|
||||
void singleton_object_t::test<3>()
|
||||
{
|
||||
//Construct the instance
|
||||
LLSingletonTest::getInstance();
|
||||
ensure(LLSingletonTest::instanceExists());
|
||||
|
||||
//Construct it again.
|
||||
LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
|
||||
ensure(singleton_test);
|
||||
ensure(LLSingletonTest::instanceExists());
|
||||
}
|
||||
//Delete the instance
|
||||
LLSingletonTest::deleteSingleton();
|
||||
ensure(!LLSingletonTest::instanceExists());
|
||||
|
||||
//Construct it again.
|
||||
LLSingletonTest* singleton_test = LLSingletonTest::getInstance();
|
||||
ensure(singleton_test);
|
||||
ensure(LLSingletonTest::instanceExists());
|
||||
}
|
||||
|
||||
#define TESTS(CLS, OTHER, N0, N1, N2, N3) \
|
||||
template<> template<> \
|
||||
void singleton_object_t::test<N0>() \
|
||||
{ \
|
||||
set_test_name("just " #CLS); \
|
||||
CLS::sDepFlag = CLS::DEP_NONE; \
|
||||
OTHER::sDepFlag = OTHER::DEP_NONE; \
|
||||
sLog.clear(); \
|
||||
\
|
||||
(void)CLS::instance(); \
|
||||
ensure_equals(sLog, #CLS "i" #CLS); \
|
||||
LLSingletonBase::cleanupAll(); \
|
||||
ensure_equals(sLog, #CLS "i" #CLS "x" #CLS); \
|
||||
LLSingletonBase::deleteAll(); \
|
||||
ensure_equals(sLog, #CLS "i" #CLS "x" #CLS "~" #CLS); \
|
||||
} \
|
||||
\
|
||||
template<> template<> \
|
||||
void singleton_object_t::test<N1>() \
|
||||
{ \
|
||||
set_test_name(#CLS " ctor depends " #OTHER); \
|
||||
CLS::sDepFlag = CLS::DEP_CTOR; \
|
||||
OTHER::sDepFlag = OTHER::DEP_NONE; \
|
||||
sLog.clear(); \
|
||||
\
|
||||
(void)CLS::instance(); \
|
||||
ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS); \
|
||||
LLSingletonBase::cleanupAll(); \
|
||||
ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "x" #OTHER); \
|
||||
LLSingletonBase::deleteAll(); \
|
||||
ensure_equals(sLog, #CLS #OTHER "i" #OTHER "i" #CLS "x" #CLS "x" #OTHER "~" #CLS "~" #OTHER); \
|
||||
} \
|
||||
\
|
||||
template<> template<> \
|
||||
void singleton_object_t::test<N2>() \
|
||||
{ \
|
||||
set_test_name(#CLS " init depends " #OTHER); \
|
||||
CLS::sDepFlag = CLS::DEP_INIT; \
|
||||
OTHER::sDepFlag = OTHER::DEP_NONE; \
|
||||
sLog.clear(); \
|
||||
\
|
||||
(void)CLS::instance(); \
|
||||
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \
|
||||
LLSingletonBase::cleanupAll(); \
|
||||
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER); \
|
||||
LLSingletonBase::deleteAll(); \
|
||||
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER "~" #CLS "~" #OTHER); \
|
||||
} \
|
||||
\
|
||||
template<> template<> \
|
||||
void singleton_object_t::test<N3>() \
|
||||
{ \
|
||||
set_test_name(#CLS " circular init"); \
|
||||
CLS::sDepFlag = CLS::DEP_INIT; \
|
||||
OTHER::sDepFlag = OTHER::DEP_CTOR; \
|
||||
sLog.clear(); \
|
||||
\
|
||||
(void)CLS::instance(); \
|
||||
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER); \
|
||||
LLSingletonBase::cleanupAll(); \
|
||||
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER); \
|
||||
LLSingletonBase::deleteAll(); \
|
||||
ensure_equals(sLog, #CLS "i" #CLS #OTHER "i" #OTHER "x" #CLS "x" #OTHER "~" #CLS "~" #OTHER); \
|
||||
}
|
||||
|
||||
TESTS(A, B, 4, 5, 6, 7)
|
||||
TESTS(B, A, 8, 9, 10, 11)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include "test_httprequestqueue.hpp"
|
||||
|
||||
#include "llproxy.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
unsigned long ssl_thread_id_callback(void);
|
||||
void ssl_locking_callback(int mode, int type, const char * file, int line);
|
||||
|
|
@ -101,7 +102,7 @@ void init_curl()
|
|||
|
||||
void term_curl()
|
||||
{
|
||||
LLProxy::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLProxy);
|
||||
|
||||
CRYPTO_set_locking_callback(NULL);
|
||||
for (int i(0); i < ssl_mutex_count; ++i)
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include "llhttpsdhandler.h"
|
||||
#include "httpcommon.h"
|
||||
#include "httpresponse.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
|
@ -623,7 +624,7 @@ void LLCrashLogger::commonCleanup()
|
|||
{
|
||||
term_curl();
|
||||
LLError::logToFile(""); //close crashreport.log
|
||||
LLProxy::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLProxy);
|
||||
}
|
||||
|
||||
void LLCrashLogger::init_curl()
|
||||
|
|
|
|||
|
|
@ -42,19 +42,12 @@ public:
|
|||
virtual void onEconomyDataChange() = 0;
|
||||
};
|
||||
|
||||
class LLGlobalEconomy
|
||||
class LLGlobalEconomy: public LLSingleton<LLGlobalEconomy>
|
||||
{
|
||||
public:
|
||||
LLGlobalEconomy();
|
||||
virtual ~LLGlobalEconomy();
|
||||
|
||||
// This class defines its singleton internally as a typedef instead of inheriting from
|
||||
// LLSingleton like most others because the LLRegionEconomy sub-class might also
|
||||
// become a singleton and this pattern will more easily disambiguate them.
|
||||
typedef LLSingleton<LLGlobalEconomy> Singleton;
|
||||
|
||||
void initSingleton() { }
|
||||
|
||||
virtual void print();
|
||||
|
||||
void addObserver(LLEconomyObserver* observer);
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@
|
|||
#include "v4math.h"
|
||||
#include "lltransfertargetvfile.h"
|
||||
#include "llcorehttputil.h"
|
||||
#include "llpounceable.h"
|
||||
|
||||
// Constants
|
||||
//const char* MESSAGE_LOG_FILENAME = "message.log";
|
||||
|
|
@ -1724,7 +1725,9 @@ std::ostream& operator<<(std::ostream& s, LLMessageSystem &msg)
|
|||
return s;
|
||||
}
|
||||
|
||||
LLMessageSystem *gMessageSystem = NULL;
|
||||
// LLPounceable supports callWhenReady(), to permit clients to queue up (e.g.)
|
||||
// callback registrations for when gMessageSystem is first assigned
|
||||
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
|
||||
|
||||
// update appropriate ping info
|
||||
void process_complete_ping_check(LLMessageSystem *msgsystem, void** /*user_data*/)
|
||||
|
|
@ -2641,7 +2644,7 @@ void end_messaging_system(bool print_summary)
|
|||
LL_INFOS("Messaging") << str.str().c_str() << LL_ENDL;
|
||||
}
|
||||
|
||||
delete gMessageSystem;
|
||||
delete static_cast<LLMessageSystem*>(gMessageSystem);
|
||||
gMessageSystem = NULL;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
|
||||
#include "llstoredmessage.h"
|
||||
#include "boost/function.hpp"
|
||||
#include "llpounceable.h"
|
||||
|
||||
const U32 MESSAGE_MAX_STRINGS_LENGTH = 64;
|
||||
const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192;
|
||||
|
|
@ -832,7 +833,7 @@ private:
|
|||
|
||||
|
||||
// external hook into messaging system
|
||||
extern LLMessageSystem *gMessageSystem;
|
||||
extern LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
|
||||
|
||||
// Must specific overall system version, which is used to determine
|
||||
// if a patch is available in the message template checksum verification.
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@
|
|||
|
||||
#include "lliosocket.h"
|
||||
#include "stringize.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
namespace tut
|
||||
{
|
||||
|
|
@ -66,7 +67,7 @@ namespace tut
|
|||
~HTTPClientTestData()
|
||||
{
|
||||
delete mClientPump;
|
||||
LLProxy::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLProxy);
|
||||
apr_pool_destroy(mPool);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -31,11 +31,12 @@
|
|||
#include "llhost.h"
|
||||
#include "message.h"
|
||||
#include "llsd.h"
|
||||
#include "llpounceable.h"
|
||||
|
||||
#include "llhost.cpp" // Needed for copy operator
|
||||
#include "net.cpp" // Needed by LLHost.
|
||||
|
||||
LLMessageSystem * gMessageSystem = NULL;
|
||||
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
|
||||
|
||||
// sensor test doubles
|
||||
bool gClearRecvWasCalled = false;
|
||||
|
|
|
|||
|
|
@ -33,8 +33,9 @@
|
|||
#include "message.h"
|
||||
#include "llmessageconfig.h"
|
||||
#include "llhttpnode_stub.cpp"
|
||||
#include "llpounceable.h"
|
||||
|
||||
LLMessageSystem* gMessageSystem = NULL;
|
||||
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
|
||||
|
||||
LLMessageConfig::SenderTrust
|
||||
LLMessageConfig::getSenderTrustedness(const std::string& msg_name)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "llsingleton.h"
|
||||
#include "llui.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
class Hunspell;
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
#include "llflyoutbutton.h"
|
||||
#include "llsearcheditor.h"
|
||||
#include "lltoolbar.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
// for XUIParse
|
||||
#include "llquaternion.h"
|
||||
|
|
@ -208,7 +209,7 @@ void LLUI::initClass(const settings_map_t& settings,
|
|||
|
||||
void LLUI::cleanupClass()
|
||||
{
|
||||
LLRender2D::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLRender2D);
|
||||
}
|
||||
|
||||
void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup, const clear_popups_t& clear_popups)
|
||||
|
|
|
|||
|
|
@ -344,95 +344,6 @@ private:
|
|||
|
||||
// Moved LLLocalClipRect to lllocalcliprect.h
|
||||
|
||||
class LLCallbackRegistry
|
||||
{
|
||||
public:
|
||||
typedef boost::signals2::signal<void()> callback_signal_t;
|
||||
|
||||
void registerCallback(const callback_signal_t::slot_type& slot)
|
||||
{
|
||||
mCallbacks.connect(slot);
|
||||
}
|
||||
|
||||
void fireCallbacks()
|
||||
{
|
||||
mCallbacks();
|
||||
}
|
||||
|
||||
private:
|
||||
callback_signal_t mCallbacks;
|
||||
};
|
||||
|
||||
class LLInitClassList :
|
||||
public LLCallbackRegistry,
|
||||
public LLSingleton<LLInitClassList>
|
||||
{
|
||||
friend class LLSingleton<LLInitClassList>;
|
||||
private:
|
||||
LLInitClassList() {}
|
||||
};
|
||||
|
||||
class LLDestroyClassList :
|
||||
public LLCallbackRegistry,
|
||||
public LLSingleton<LLDestroyClassList>
|
||||
{
|
||||
friend class LLSingleton<LLDestroyClassList>;
|
||||
private:
|
||||
LLDestroyClassList() {}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class LLRegisterWith
|
||||
{
|
||||
public:
|
||||
LLRegisterWith(boost::function<void ()> func)
|
||||
{
|
||||
T::instance().registerCallback(func);
|
||||
}
|
||||
|
||||
// this avoids a MSVC bug where non-referenced static members are "optimized" away
|
||||
// even if their constructors have side effects
|
||||
S32 reference()
|
||||
{
|
||||
S32 dummy;
|
||||
dummy = 0;
|
||||
return dummy;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class LLInitClass
|
||||
{
|
||||
public:
|
||||
LLInitClass() { sRegister.reference(); }
|
||||
|
||||
static LLRegisterWith<LLInitClassList> sRegister;
|
||||
private:
|
||||
|
||||
static void initClass()
|
||||
{
|
||||
LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class LLDestroyClass
|
||||
{
|
||||
public:
|
||||
LLDestroyClass() { sRegister.reference(); }
|
||||
|
||||
static LLRegisterWith<LLDestroyClassList> sRegister;
|
||||
private:
|
||||
|
||||
static void destroyClass()
|
||||
{
|
||||
LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T> LLRegisterWith<LLInitClassList> LLInitClass<T>::sRegister(&T::initClass);
|
||||
template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sRegister(&T::destroyClass);
|
||||
|
||||
// useful parameter blocks
|
||||
struct TimeIntervalParam : public LLInitParam::ChoiceBlock<TimeIntervalParam>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "llcachename.h"
|
||||
#include "lluuid.h"
|
||||
#include "message.h"
|
||||
#include "llpounceable.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
|
@ -165,7 +166,7 @@ LLFontGL* LLFontGL::getFontDefault()
|
|||
char const* const _PREHASH_AgentData = (char *)"AgentData";
|
||||
char const* const _PREHASH_AgentID = (char *)"AgentID";
|
||||
|
||||
LLMessageSystem* gMessageSystem = NULL;
|
||||
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
|
||||
|
||||
//
|
||||
// Stub implementation for LLMessageSystem
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@
|
|||
#include "llviewerinventory.h"
|
||||
#include "llavatarappearancedefines.h"
|
||||
#include "llwearabledata.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
|
||||
class LLInventoryItem;
|
||||
class LLVOAvatarSelf;
|
||||
|
|
|
|||
|
|
@ -227,6 +227,7 @@
|
|||
#include "llsecapi.h"
|
||||
#include "llmachineid.h"
|
||||
#include "llmainlooprepeater.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
#include "llcoproceduremanager.h"
|
||||
#include "llviewereventrecorder.h"
|
||||
|
|
@ -1744,7 +1745,7 @@ bool LLAppViewer::cleanup()
|
|||
gTransferManager.cleanup();
|
||||
#endif
|
||||
|
||||
LLLocalBitmapMgr::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLLocalBitmapMgr);
|
||||
|
||||
// Note: this is where gWorldMap used to be deleted.
|
||||
|
||||
|
|
@ -1853,11 +1854,11 @@ bool LLAppViewer::cleanup()
|
|||
|
||||
LLViewerObject::cleanupVOClasses();
|
||||
|
||||
LLAvatarAppearance::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLAvatarAppearance);
|
||||
|
||||
LLAvatarAppearance::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLAvatarAppearance);
|
||||
|
||||
LLPostProcess::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLPostProcess);
|
||||
|
||||
LLTracker::cleanupInstance();
|
||||
|
||||
|
|
@ -1883,12 +1884,12 @@ bool LLAppViewer::cleanup()
|
|||
|
||||
//end_messaging_system();
|
||||
|
||||
LLFollowCamMgr::cleanupClass();
|
||||
//LLVolumeMgr::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLFollowCamMgr);
|
||||
//SUBSYSTEM_CLEANUP(LLVolumeMgr);
|
||||
LLPrimitive::cleanupVolumeManager();
|
||||
LLWorldMapView::cleanupClass();
|
||||
LLFolderViewItem::cleanupClass();
|
||||
LLUI::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLWorldMapView);
|
||||
SUBSYSTEM_CLEANUP(LLFolderViewItem);
|
||||
SUBSYSTEM_CLEANUP(LLUI);
|
||||
|
||||
//
|
||||
// Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles).
|
||||
|
|
@ -1897,7 +1898,7 @@ bool LLAppViewer::cleanup()
|
|||
|
||||
//
|
||||
LL_INFOS() << "Cleaning up VFS" << LL_ENDL;
|
||||
LLVFile::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLVFile);
|
||||
|
||||
LL_INFOS() << "Saving Data" << LL_ENDL;
|
||||
|
||||
|
|
@ -2000,9 +2001,9 @@ bool LLAppViewer::cleanup()
|
|||
// Non-LLCurl libcurl library
|
||||
mAppCoreHttp.cleanup();
|
||||
|
||||
LLFilePickerThread::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLFilePickerThread);
|
||||
|
||||
//MUST happen AFTER LLCurl::cleanupClass
|
||||
//MUST happen AFTER SUBSYSTEM_CLEANUP(LLCurl)
|
||||
delete sTextureCache;
|
||||
sTextureCache = NULL;
|
||||
delete sTextureFetch;
|
||||
|
|
@ -2031,17 +2032,17 @@ bool LLAppViewer::cleanup()
|
|||
LL_INFOS() << "Cleaning up Media and Textures" << LL_ENDL;
|
||||
|
||||
//Note:
|
||||
//LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown()
|
||||
//SUBSYSTEM_CLEANUP(LLViewerMedia) has to be put before gTextureList.shutdown()
|
||||
//because some new image might be generated during cleaning up media. --bao
|
||||
LLViewerMedia::cleanupClass();
|
||||
LLViewerParcelMedia::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLViewerMedia);
|
||||
SUBSYSTEM_CLEANUP(LLViewerParcelMedia);
|
||||
gTextureList.shutdown(); // shutdown again in case a callback added something
|
||||
LLUIImageList::getInstance()->cleanUp();
|
||||
|
||||
// This should eventually be done in LLAppViewer
|
||||
LLImage::cleanupClass();
|
||||
LLVFSThread::cleanupClass();
|
||||
LLLFSThread::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLImage);
|
||||
SUBSYSTEM_CLEANUP(LLVFSThread);
|
||||
SUBSYSTEM_CLEANUP(LLLFSThread);
|
||||
|
||||
#ifndef LL_RELEASE_FOR_DOWNLOAD
|
||||
LL_INFOS() << "Auditing VFS" << LL_ENDL;
|
||||
|
|
@ -2084,10 +2085,10 @@ bool LLAppViewer::cleanup()
|
|||
LL_INFOS() << "File launched." << LL_ENDL;
|
||||
}
|
||||
LL_INFOS() << "Cleaning up LLProxy." << LL_ENDL;
|
||||
LLProxy::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLProxy);
|
||||
LLCore::LLHttp::cleanup();
|
||||
|
||||
LLWearableType::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLWearableType);
|
||||
|
||||
LLMainLoopRepeater::instance().stop();
|
||||
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "llinventoryobserver.h"
|
||||
#include "llinventorymodel.h"
|
||||
#include "llviewerinventory.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
|
||||
class LLMenuItemCallGL;
|
||||
class LLToggleableMenu;
|
||||
|
|
|
|||
|
|
@ -992,7 +992,7 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata)
|
|||
{
|
||||
std::string name = floaterp->getChild<LLUICtrl>("name_form")->getValue().asString();
|
||||
std::string desc = floaterp->getChild<LLUICtrl>("description_form")->getValue().asString();
|
||||
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
|
||||
S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
|
||||
|
||||
LLResourceUploadInfo::ptr_t assetUpdloadInfo(new LLResourceUploadInfo(
|
||||
floaterp->mTransactionID, LLAssetType::AT_ANIMATION,
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ LLFloaterIMContainer::~LLFloaterIMContainer()
|
|||
gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed());
|
||||
gSavedPerAccountSettings.setBOOL("ConversationsParticipantListCollapsed", !isParticipantListExpanded());
|
||||
|
||||
if (!LLSingleton<LLIMMgr>::destroyed())
|
||||
if (LLIMMgr::instanceExists())
|
||||
{
|
||||
LLIMMgr::getInstance()->removeSessionObserver(this);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -123,7 +123,7 @@ BOOL LLFloaterNameDesc::postBuild()
|
|||
// Cancel button
|
||||
getChild<LLUICtrl>("cancel_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnCancel, this));
|
||||
|
||||
getChild<LLUICtrl>("ok_btn")->setLabelArg("[AMOUNT]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload() ));
|
||||
getChild<LLUICtrl>("ok_btn")->setLabelArg("[AMOUNT]", llformat("%d", LLGlobalEconomy::getInstance()->getPriceUpload() ));
|
||||
|
||||
setDefaultBtn("ok_btn");
|
||||
|
||||
|
|
@ -162,7 +162,7 @@ void LLFloaterNameDesc::onBtnOK( )
|
|||
getChildView("ok_btn")->setEnabled(FALSE); // don't allow inadvertent extra uploads
|
||||
|
||||
LLAssetStorage::LLStoreAssetCallback callback = NULL;
|
||||
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); // kinda hack - assumes that unsubclassed LLFloaterNameDesc is only used for uploading chargeable assets, which it is right now (it's only used unsubclassed for the sound upload dialog, and THAT should be a subclass).
|
||||
S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload(); // kinda hack - assumes that unsubclassed LLFloaterNameDesc is only used for uploading chargeable assets, which it is right now (it's only used unsubclassed for the sound upload dialog, and THAT should be a subclass).
|
||||
|
||||
if (can_afford_transaction(expected_upload_cost))
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@
|
|||
|
||||
#include "llpanel.h"
|
||||
#include "llnotifications.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
|
||||
|
||||
class LLHints : public LLInitClass<LLHints>
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
#include "lllogchat.h"
|
||||
#include "llvoicechannel.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
|
||||
#include "llcoros.h"
|
||||
#include "lleventcoro.h"
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@
|
|||
#include "pipeline.h"
|
||||
|
||||
#include <boost/tokenizer.hpp>
|
||||
#include <boost/bind.hpp>
|
||||
|
||||
#include "lldispatcher.h"
|
||||
#include "llxfermanager.h"
|
||||
|
|
@ -146,22 +147,6 @@ std::string LLMute::getDisplayType() const
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
/* static */
|
||||
LLMuteList* LLMuteList::getInstance()
|
||||
{
|
||||
// Register callbacks at the first time that we find that the message system has been created.
|
||||
static BOOL registered = FALSE;
|
||||
if( !registered && gMessageSystem != NULL)
|
||||
{
|
||||
registered = TRUE;
|
||||
// Register our various callbacks
|
||||
gMessageSystem->setHandlerFuncFast(_PREHASH_MuteListUpdate, processMuteListUpdate);
|
||||
gMessageSystem->setHandlerFuncFast(_PREHASH_UseCachedMuteList, processUseCachedMuteList);
|
||||
}
|
||||
return LLSingleton<LLMuteList>::getInstance(); // Call the "base" implementation.
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
// LLMuteList()
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
@ -169,6 +154,18 @@ LLMuteList::LLMuteList() :
|
|||
mIsLoaded(FALSE)
|
||||
{
|
||||
gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList);
|
||||
|
||||
// Register our callbacks. We may be constructed before gMessageSystem, so
|
||||
// use callWhenReady() to register them as soon as gMessageSystem becomes
|
||||
// available.
|
||||
// When using bind(), must be explicit about default arguments such as
|
||||
// that last NULL.
|
||||
gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1,
|
||||
_PREHASH_MuteListUpdate, processMuteListUpdate,
|
||||
static_cast<void**>(NULL)));
|
||||
gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1,
|
||||
_PREHASH_UseCachedMuteList, processUseCachedMuteList,
|
||||
static_cast<void**>(NULL)));
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -84,11 +84,6 @@ public:
|
|||
LLMuteList();
|
||||
~LLMuteList();
|
||||
|
||||
// Implemented locally so that we can perform some delayed initialization.
|
||||
// Callers should be careful to call this one and not LLSingleton<LLMuteList>::getInstance()
|
||||
// which would circumvent that mechanism. -MG
|
||||
static LLMuteList* getInstance();
|
||||
|
||||
void addObserver(LLMuteListObserver* observer);
|
||||
void removeObserver(LLMuteListObserver* observer);
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
#include "llpanel.h"
|
||||
#include "llbutton.h"
|
||||
#include "lllayoutstack.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
|
||||
class LLLocationInputCtrl;
|
||||
class LLMenuGL;
|
||||
|
|
|
|||
|
|
@ -1294,7 +1294,7 @@ void LLPanelMainInventory::setUploadCostIfNeeded()
|
|||
LLMenuItemBranchGL* upload_menu = mMenuAdd->findChild<LLMenuItemBranchGL>("upload");
|
||||
if(upload_menu)
|
||||
{
|
||||
S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
|
||||
S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
|
||||
std::string cost_str;
|
||||
|
||||
// getPriceUpload() returns -1 if no data available yet.
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ BOOL LLPanelSnapshotInventory::postBuild()
|
|||
// virtual
|
||||
void LLPanelSnapshotInventory::onOpen(const LLSD& key)
|
||||
{
|
||||
getChild<LLUICtrl>("hint_lbl")->setTextArg("[UPLOAD_COST]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload()));
|
||||
getChild<LLUICtrl>("hint_lbl")->setTextArg("[UPLOAD_COST]", llformat("%d", LLGlobalEconomy::getInstance()->getPriceUpload()));
|
||||
LLPanelSnapshot::onOpen(key);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -75,12 +75,12 @@ LLPanelSnapshotOptions::LLPanelSnapshotOptions()
|
|||
mCommitCallbackRegistrar.add("Snapshot.SendToFacebook", boost::bind(&LLPanelSnapshotOptions::onSendToFacebook, this));
|
||||
mCommitCallbackRegistrar.add("Snapshot.SendToTwitter", boost::bind(&LLPanelSnapshotOptions::onSendToTwitter, this));
|
||||
mCommitCallbackRegistrar.add("Snapshot.SendToFlickr", boost::bind(&LLPanelSnapshotOptions::onSendToFlickr, this));
|
||||
LLGlobalEconomy::Singleton::getInstance()->addObserver(this);
|
||||
LLGlobalEconomy::getInstance()->addObserver(this);
|
||||
}
|
||||
|
||||
LLPanelSnapshotOptions::~LLPanelSnapshotOptions()
|
||||
{
|
||||
LLGlobalEconomy::Singleton::getInstance()->removeObserver(this);
|
||||
LLGlobalEconomy::getInstance()->removeObserver(this);
|
||||
}
|
||||
|
||||
// virtual
|
||||
|
|
@ -97,7 +97,7 @@ void LLPanelSnapshotOptions::onOpen(const LLSD& key)
|
|||
|
||||
void LLPanelSnapshotOptions::updateUploadCost()
|
||||
{
|
||||
S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
|
||||
S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
|
||||
getChild<LLUICtrl>("save_to_inventory_btn")->setLabelArg("[AMOUNT]", llformat("%d", upload_cost));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#define LLPANELTOPINFOBAR_H_
|
||||
|
||||
#include "llpanel.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
|
||||
class LLButton;
|
||||
class LLTextBox;
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@
|
|||
#define LL_LLSEARCHHISTORY_H
|
||||
|
||||
#include "llsingleton.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
#include "llui.h"
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1032,7 +1032,7 @@ void LLSnapshotLivePreview::saveTexture()
|
|||
LLAgentUI::buildLocationString(pos_string, LLAgentUI::LOCATION_FORMAT_FULL);
|
||||
std::string who_took_it;
|
||||
LLAgentUI::buildFullname(who_took_it);
|
||||
S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
|
||||
S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
|
||||
std::string name = "Snapshot: " + pos_string;
|
||||
std::string desc = "Taken by " + who_took_it + " at " + pos_string;
|
||||
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@
|
|||
#include "llstartuplistener.h"
|
||||
#include "lltoolbarview.h"
|
||||
#include "llexperiencelog.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
#include "lldxhardware.h"
|
||||
|
|
@ -2804,7 +2805,7 @@ void LLStartUp::initExperiences()
|
|||
|
||||
void LLStartUp::cleanupNameCache()
|
||||
{
|
||||
LLAvatarNameCache::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLAvatarNameCache);
|
||||
|
||||
delete gCacheName;
|
||||
gCacheName = NULL;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,7 @@
|
|||
#include "llscreenchannel.h"
|
||||
#include "llsyswellitem.h"
|
||||
#include "lltransientdockablefloater.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
|
||||
class LLAvatarName;
|
||||
class LLChiclet;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
#include "llinventory.h"
|
||||
#include "llframetimer.h"
|
||||
#include "llwearable.h"
|
||||
#include "llui.h" //for LLDestroyClass
|
||||
#include "llinitdestroyclass.h" //for LLDestroyClass
|
||||
|
||||
#include <boost/signals2.hpp> // boost::signals2::trackable
|
||||
|
||||
|
|
|
|||
|
|
@ -133,6 +133,7 @@
|
|||
#include "llpathfindingmanager.h"
|
||||
#include "llstartup.h"
|
||||
#include "boost/unordered_map.hpp"
|
||||
#include "llcleanup.h"
|
||||
|
||||
using namespace LLAvatarAppearanceDefines;
|
||||
|
||||
|
|
@ -8482,7 +8483,7 @@ class LLWorldPostProcess : public view_listener_t
|
|||
|
||||
void handle_flush_name_caches()
|
||||
{
|
||||
LLAvatarNameCache::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLAvatarNameCache);
|
||||
if (gCacheName) gCacheName->clear();
|
||||
}
|
||||
|
||||
|
|
@ -8526,7 +8527,7 @@ class LLToggleUIHints : public view_listener_t
|
|||
|
||||
void LLUploadCostCalculator::calculateCost()
|
||||
{
|
||||
S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
|
||||
S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
|
||||
|
||||
// getPriceUpload() returns -1 if no data available yet.
|
||||
if(upload_cost >= 0)
|
||||
|
|
|
|||
|
|
@ -6274,9 +6274,9 @@ void process_frozen_message(LLMessageSystem *msgsystem, void **user_data)
|
|||
// do some extra stuff once we get our economy data
|
||||
void process_economy_data(LLMessageSystem *msg, void** /*user_data*/)
|
||||
{
|
||||
LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::Singleton::getInstance());
|
||||
LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::getInstance());
|
||||
|
||||
S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload();
|
||||
S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();
|
||||
|
||||
LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL;
|
||||
|
||||
|
|
|
|||
|
|
@ -102,6 +102,7 @@
|
|||
#include "llmediaentry.h"
|
||||
#include "llfloaterperms.h"
|
||||
#include "llvocache.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
//#define DEBUG_UPDATE_TYPE
|
||||
|
||||
|
|
@ -529,11 +530,11 @@ void LLViewerObject::initVOClasses()
|
|||
|
||||
void LLViewerObject::cleanupVOClasses()
|
||||
{
|
||||
LLVOGrass::cleanupClass();
|
||||
LLVOWater::cleanupClass();
|
||||
LLVOTree::cleanupClass();
|
||||
LLVOAvatar::cleanupClass();
|
||||
LLVOVolume::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLVOGrass);
|
||||
SUBSYSTEM_CLEANUP(LLVOWater);
|
||||
SUBSYSTEM_CLEANUP(LLVOTree);
|
||||
SUBSYSTEM_CLEANUP(LLVOAvatar);
|
||||
SUBSYSTEM_CLEANUP(LLVOVolume);
|
||||
|
||||
sObjectDataMap.clear();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -208,6 +208,7 @@
|
|||
#include "llwindowlistener.h"
|
||||
#include "llviewerwindowlistener.h"
|
||||
#include "llpaneltopinfobar.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
#if LL_WINDOWS
|
||||
#include <tchar.h> // For Unicode conversion methods
|
||||
|
|
@ -2158,7 +2159,7 @@ void LLViewerWindow::shutdownGL()
|
|||
// Shutdown GL cleanly. Order is very important here.
|
||||
//--------------------------------------------------------
|
||||
LLFontGL::destroyDefaultFonts();
|
||||
LLFontManager::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLFontManager);
|
||||
stop_glerror();
|
||||
|
||||
gSky.cleanup();
|
||||
|
|
@ -2181,7 +2182,7 @@ void LLViewerWindow::shutdownGL()
|
|||
LLWorldMapView::cleanupTextures();
|
||||
|
||||
LLViewerTextureManager::cleanup() ;
|
||||
LLImageGL::cleanupClass() ;
|
||||
SUBSYSTEM_CLEANUP(LLImageGL) ;
|
||||
|
||||
LL_INFOS() << "All textures and llimagegl images are destroyed!" << LL_ENDL ;
|
||||
|
||||
|
|
@ -2194,7 +2195,7 @@ void LLViewerWindow::shutdownGL()
|
|||
|
||||
gGL.shutdown();
|
||||
|
||||
LLVertexBuffer::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLVertexBuffer);
|
||||
|
||||
LL_INFOS() << "LLVertexBuffer cleaned." << LL_ENDL ;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@
|
|||
#include "llpathfindingpathtool.h"
|
||||
#include "llscenemonitor.h"
|
||||
#include "llprogressview.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
#ifdef _DEBUG
|
||||
// Debug indices is disabled for now for debug performance - djs 4/24/02
|
||||
|
|
@ -7385,7 +7386,7 @@ void LLPipeline::doResetVertexBuffers(bool forced)
|
|||
}
|
||||
LLVOPartGroup::destroyGL();
|
||||
|
||||
LLVertexBuffer::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLVertexBuffer);
|
||||
|
||||
//delete all name pool caches
|
||||
LLGLNamePool::cleanupPools();
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@
|
|||
#include "../llagent.h"
|
||||
#include "message.h"
|
||||
#include "llurlentry.h"
|
||||
#include "llpounceable.h"
|
||||
|
||||
namespace {
|
||||
const LLUUID TEST_PARCEL_ID("11111111-1111-1111-1111-111111111111");
|
||||
|
|
@ -62,7 +63,7 @@ void LLMessageSystem::addUUID(char const *,LLUUID const &) { }
|
|||
void LLMessageSystem::addUUIDFast(char const *,LLUUID const &) { }
|
||||
void LLMessageSystem::nextBlockFast(char const *) { }
|
||||
void LLMessageSystem::newMessage(char const *) { }
|
||||
LLMessageSystem * gMessageSystem;
|
||||
LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;
|
||||
char const* const _PREHASH_AgentID = 0; // never dereferenced during this test
|
||||
char const* const _PREHASH_AgentData = 0; // never dereferenced during this test
|
||||
LLAgent gAgent;
|
||||
|
|
|
|||
|
|
@ -103,7 +103,7 @@ namespace tut
|
|||
~LLMessageSystemTestData()
|
||||
{
|
||||
// not end_messaging_system()
|
||||
delete gMessageSystem;
|
||||
delete static_cast<LLMessageSystem*>(gMessageSystem);
|
||||
gMessageSystem = NULL;
|
||||
|
||||
// rm contents of temp dir
|
||||
|
|
|
|||
Loading…
Reference in New Issue