Merge viewer-lynx
commit
c182741596
|
|
@ -205,6 +205,12 @@ Ansariel Hiller
|
|||
MAINT-6636
|
||||
MAINT-6744
|
||||
MAINT-6752
|
||||
MAINT-6773
|
||||
MAINT-6906
|
||||
MAINT-6911
|
||||
MAINT-6917
|
||||
STORM-2140
|
||||
MAINT-6912
|
||||
Aralara Rajal
|
||||
Arare Chantilly
|
||||
CHUIBUG-191
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -127,8 +127,7 @@ class LLAvatarAppearanceDictionary : public LLSingleton<LLAvatarAppearanceDictio
|
|||
//--------------------------------------------------------------------
|
||||
// Constructors and Destructors
|
||||
//--------------------------------------------------------------------
|
||||
public:
|
||||
LLAvatarAppearanceDictionary();
|
||||
LLSINGLETON(LLAvatarAppearanceDictionary);
|
||||
virtual ~LLAvatarAppearanceDictionary();
|
||||
private:
|
||||
void createAssociations();
|
||||
|
|
|
|||
|
|
@ -293,9 +293,9 @@ protected:
|
|||
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
class LLTexLayerStaticImageList : public LLSingleton<LLTexLayerStaticImageList>
|
||||
{
|
||||
public:
|
||||
LLTexLayerStaticImageList();
|
||||
LLSINGLETON(LLTexLayerStaticImageList);
|
||||
~LLTexLayerStaticImageList();
|
||||
public:
|
||||
LLGLTexture* getTexture(const std::string& file_name, BOOL is_mask);
|
||||
LLImageTGA* getImageTGA(const std::string& file_name);
|
||||
void deleteCachedImages();
|
||||
|
|
|
|||
|
|
@ -71,8 +71,7 @@ struct WearableEntry : public LLDictionaryEntry
|
|||
class LLWearableDictionary : public LLSingleton<LLWearableDictionary>,
|
||||
public LLDictionary<LLWearableType::EType, WearableEntry>
|
||||
{
|
||||
public:
|
||||
LLWearableDictionary();
|
||||
LLSINGLETON(LLWearableDictionary);
|
||||
|
||||
// [RLVa:KB] - Checked: 2010-03-03 (RLVa-1.2.0a) | Added: RLVa-1.2.0a
|
||||
protected:
|
||||
|
|
|
|||
|
|
@ -40,8 +40,10 @@ set(llcommon_SOURCE_FILES
|
|||
llbase64.cpp
|
||||
llbitpack.cpp
|
||||
llcallbacklist.cpp
|
||||
llcleanup.cpp
|
||||
llcommon.cpp
|
||||
llcommonutils.cpp
|
||||
llcoro_get_id.cpp
|
||||
llcoros.cpp
|
||||
llcrc.cpp
|
||||
llcriticaldamp.cpp
|
||||
|
|
@ -66,7 +68,9 @@ set(llcommon_SOURCE_FILES
|
|||
llformat.cpp
|
||||
llframetimer.cpp
|
||||
llheartbeat.cpp
|
||||
llheteromap.cpp
|
||||
llinitparam.cpp
|
||||
llinitdestroyclass.cpp
|
||||
llinstancetracker.cpp
|
||||
llleap.cpp
|
||||
llleaplistener.cpp
|
||||
|
|
@ -135,8 +139,10 @@ set(llcommon_HEADER_FILES
|
|||
llbitpack.h
|
||||
llboost.h
|
||||
llcallbacklist.h
|
||||
llcleanup.h
|
||||
llcommon.h
|
||||
llcommonutils.h
|
||||
llcoro_get_id.h
|
||||
llcoros.h
|
||||
llcrc.h
|
||||
llcriticaldamp.h
|
||||
|
|
@ -168,7 +174,9 @@ set(llcommon_HEADER_FILES
|
|||
llhandle.h
|
||||
llhash.h
|
||||
llheartbeat.h
|
||||
llheteromap.h
|
||||
llindexedvector.h
|
||||
llinitdestroyclass.h
|
||||
llinitparam.h
|
||||
llinstancetracker.h
|
||||
llkeythrottle.h
|
||||
|
|
@ -185,6 +193,7 @@ set(llcommon_HEADER_FILES
|
|||
llmortician.h
|
||||
llnametable.h
|
||||
llpointer.h
|
||||
llpounceable.h
|
||||
llpredicate.h
|
||||
llpreprocessor.h
|
||||
llpriqueuemap.h
|
||||
|
|
@ -364,6 +373,8 @@ 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}")
|
||||
LL_ADD_INTEGRATION_TEST(llheteromap "" "${test_libs}")
|
||||
|
||||
## llexception_test.cpp isn't a regression test, and doesn't need to be run
|
||||
## every build. It's to help a developer make implementation choices about
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@
|
|||
|
||||
#include "google_breakpad/exception_handler.h"
|
||||
#include "stringize.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
//
|
||||
// Signal handling
|
||||
|
|
@ -181,7 +182,7 @@ LLApp::~LLApp()
|
|||
|
||||
if(mExceptionHandler != 0) delete mExceptionHandler;
|
||||
|
||||
LLCommon::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLCommon);
|
||||
}
|
||||
|
||||
// static
|
||||
|
|
|
|||
|
|
@ -63,8 +63,7 @@ struct AssetEntry : public LLDictionaryEntry
|
|||
class LLAssetDictionary : public LLSingleton<LLAssetDictionary>,
|
||||
public LLDictionary<LLAssetType::EType, AssetEntry>
|
||||
{
|
||||
public:
|
||||
LLAssetDictionary();
|
||||
LLSINGLETON(LLAssetDictionary);
|
||||
};
|
||||
|
||||
LLAssetDictionary::LLAssetDictionary()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
* @file llcleanup.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2016-08-30
|
||||
* @brief Implementation for llcleanup.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2016&license=viewerlgpl$
|
||||
* Copyright (c) 2016, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Precompiled header
|
||||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "llcleanup.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
// other Linden headers
|
||||
#include "llerror.h"
|
||||
#include "llerrorcontrol.h"
|
||||
|
||||
void log_subsystem_cleanup(const char* file, int line, const char* function,
|
||||
const char* classname)
|
||||
{
|
||||
LL_INFOS("Cleanup") << LLError::abbreviateFile(file) << "(" << line << "): "
|
||||
<< "calling " << classname << "::cleanupClass() in "
|
||||
<< function << LL_ENDL;
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
/**
|
||||
* @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 <boost/current_function.hpp>
|
||||
|
||||
// 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 { \
|
||||
log_subsystem_cleanup(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, #CLASSNAME); \
|
||||
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.
|
||||
|
||||
void log_subsystem_cleanup(const char* file, int line, const char* function,
|
||||
const char* classname);
|
||||
|
||||
#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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file llcoro_get_id.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2016-09-03
|
||||
* @brief Implementation for llcoro_get_id.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2016&license=viewerlgpl$
|
||||
* Copyright (c) 2016, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Precompiled header
|
||||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "llcoro_get_id.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
// other Linden headers
|
||||
#include "llcoros.h"
|
||||
|
||||
namespace llcoro
|
||||
{
|
||||
|
||||
id get_id()
|
||||
{
|
||||
// An instance of Current can convert to LLCoros::CoroData*, which can
|
||||
// implicitly convert to void*, which is an llcoro::id.
|
||||
return LLCoros::Current();
|
||||
}
|
||||
|
||||
} // llcoro
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* @file llcoro_get_id.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2016-09-03
|
||||
* @brief Supplement the functionality in llcoro.h.
|
||||
*
|
||||
* This is broken out as a separate header file to resolve
|
||||
* circularity: LLCoros isa LLSingleton, yet LLSingleton machinery
|
||||
* requires llcoro::get_id().
|
||||
*
|
||||
* Be very suspicious of anyone else #including this header.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2016&license=viewerlgpl$
|
||||
* Copyright (c) 2016, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLCORO_GET_ID_H)
|
||||
#define LL_LLCORO_GET_ID_H
|
||||
|
||||
namespace llcoro
|
||||
{
|
||||
|
||||
/// Get an opaque, distinct token for the running coroutine (or main).
|
||||
typedef void* id;
|
||||
id get_id();
|
||||
|
||||
} // llcoro
|
||||
|
||||
#endif /* ! defined(LL_LLCORO_GET_ID_H) */
|
||||
|
|
@ -40,32 +40,79 @@
|
|||
#include "stringize.h"
|
||||
#include "llexception.h"
|
||||
|
||||
// do nothing, when we need nothing done
|
||||
namespace {
|
||||
void no_op() {}
|
||||
} // anonymous namespace
|
||||
|
||||
// Do nothing, when we need nothing done. This is a static member of LLCoros
|
||||
// because CoroData is a private nested class.
|
||||
void LLCoros::no_cleanup(CoroData*) {}
|
||||
|
||||
// CoroData for the currently-running coroutine. Use a thread_specific_ptr
|
||||
// because each thread potentially has its own distinct pool of coroutines.
|
||||
// This thread_specific_ptr does NOT own the CoroData object! That's owned by
|
||||
// LLCoros::mCoros. It merely identifies it. For this reason we instantiate
|
||||
// it with a no-op cleanup function.
|
||||
boost::thread_specific_ptr<LLCoros::CoroData>
|
||||
LLCoros::sCurrentCoro(LLCoros::no_cleanup);
|
||||
LLCoros::Current::Current()
|
||||
{
|
||||
// Use a function-static instance so this thread_specific_ptr is
|
||||
// instantiated on demand. Since we happen to know it's consumed by
|
||||
// LLSingleton, this is likely to happen before the runtime has finished
|
||||
// initializing module-static data. For the same reason, we can't package
|
||||
// this pointer in an LLSingleton.
|
||||
|
||||
// This thread_specific_ptr does NOT own the CoroData object! That's owned
|
||||
// by LLCoros::mCoros. It merely identifies it. For this reason we
|
||||
// instantiate it with a no-op cleanup function.
|
||||
static boost::thread_specific_ptr<LLCoros::CoroData> sCurrent(LLCoros::no_cleanup);
|
||||
|
||||
// If this is the first time we're accessing sCurrent for the running
|
||||
// thread, its get() will be NULL. This could be a problem, in that
|
||||
// llcoro::get_id() would return the same (NULL) token value for the "main
|
||||
// coroutine" in every thread, whereas what we really want is a distinct
|
||||
// value for every distinct stack in the process. So if get() is NULL,
|
||||
// give it a heap CoroData: this ensures that llcoro::get_id() will return
|
||||
// distinct values.
|
||||
// This tactic is "leaky": sCurrent explicitly does not destroy any
|
||||
// CoroData to which it points, and we do NOT enter these "main coroutine"
|
||||
// CoroData instances in the LLCoros::mCoros map. They are dummy entries,
|
||||
// and they will leak at process shutdown: one CoroData per thread.
|
||||
if (! sCurrent.get())
|
||||
{
|
||||
// It's tempting to provide a distinct name for each thread's "main
|
||||
// coroutine." But as getName() has always returned the empty string
|
||||
// to mean "not in a coroutine," empty string should suffice here --
|
||||
// and truthfully the additional (thread-safe!) machinery to ensure
|
||||
// uniqueness just doesn't feel worth the trouble.
|
||||
// We use a no-op callable and a minimal stack size because, although
|
||||
// CoroData's constructor in fact initializes its mCoro with a
|
||||
// coroutine with that stack size, no one ever actually enters it by
|
||||
// calling mCoro().
|
||||
sCurrent.reset(new CoroData(0, // no prev
|
||||
"", // not a named coroutine
|
||||
no_op, // no-op callable
|
||||
1024)); // stacksize moot
|
||||
}
|
||||
|
||||
mCurrent = &sCurrent;
|
||||
}
|
||||
|
||||
//static
|
||||
LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller)
|
||||
{
|
||||
CoroData* current = sCurrentCoro.get();
|
||||
if (! current)
|
||||
{
|
||||
LL_ERRS("LLCoros") << "Calling " << caller << " from non-coroutine context!" << LL_ENDL;
|
||||
}
|
||||
CoroData* current = Current();
|
||||
// With the dummy CoroData set in LLCoros::Current::Current(), this
|
||||
// pointer should never be NULL.
|
||||
llassert_always(current);
|
||||
return *current;
|
||||
}
|
||||
|
||||
//static
|
||||
LLCoros::coro::self& LLCoros::get_self()
|
||||
{
|
||||
return *get_CoroData("get_self()").mSelf;
|
||||
CoroData& current = get_CoroData("get_self()");
|
||||
if (! current.mSelf)
|
||||
{
|
||||
LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL;
|
||||
}
|
||||
return *current.mSelf;
|
||||
}
|
||||
|
||||
//static
|
||||
|
|
@ -80,20 +127,23 @@ bool LLCoros::get_consuming()
|
|||
return get_CoroData("get_consuming()").mConsuming;
|
||||
}
|
||||
|
||||
llcoro::Suspending::Suspending():
|
||||
mSuspended(LLCoros::sCurrentCoro.get())
|
||||
llcoro::Suspending::Suspending()
|
||||
{
|
||||
// Revert mCurrentCoro to the value it had at the moment we last switched
|
||||
LLCoros::Current current;
|
||||
// Remember currently-running coroutine: we're about to suspend it.
|
||||
mSuspended = current;
|
||||
// Revert Current to the value it had at the moment we last switched
|
||||
// into this coroutine.
|
||||
LLCoros::sCurrentCoro.reset(mSuspended->mPrev);
|
||||
current.reset(mSuspended->mPrev);
|
||||
}
|
||||
|
||||
llcoro::Suspending::~Suspending()
|
||||
{
|
||||
LLCoros::Current current;
|
||||
// Okay, we're back, update our mPrev
|
||||
mSuspended->mPrev = LLCoros::sCurrentCoro.get();
|
||||
// and reinstate our sCurrentCoro.
|
||||
LLCoros::sCurrentCoro.reset(mSuspended);
|
||||
mSuspended->mPrev = current;
|
||||
// and reinstate our Current.
|
||||
current.reset(mSuspended);
|
||||
}
|
||||
|
||||
LLCoros::LLCoros():
|
||||
|
|
@ -213,13 +263,7 @@ bool LLCoros::kill(const std::string& name)
|
|||
|
||||
std::string LLCoros::getName() const
|
||||
{
|
||||
CoroData* current = sCurrentCoro.get();
|
||||
if (! current)
|
||||
{
|
||||
// not in a coroutine
|
||||
return "";
|
||||
}
|
||||
return current->mName;
|
||||
return Current()->mName;
|
||||
}
|
||||
|
||||
void LLCoros::setStackSize(S32 stacksize)
|
||||
|
|
@ -229,8 +273,8 @@ void LLCoros::setStackSize(S32 stacksize)
|
|||
}
|
||||
|
||||
// Top-level wrapper around caller's coroutine callable. This function accepts
|
||||
// the coroutine library's implicit coro::self& parameter and sets sCurrentSelf
|
||||
// but does not pass it down to the caller's callable.
|
||||
// the coroutine library's implicit coro::self& parameter and saves it, but
|
||||
// does not pass it down to the caller's callable.
|
||||
void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& callable)
|
||||
{
|
||||
// capture the 'self' param in CoroData
|
||||
|
|
@ -254,8 +298,8 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla
|
|||
CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName));
|
||||
}
|
||||
// This cleanup isn't perfectly symmetrical with the way we initially set
|
||||
// data->mPrev, but this is our last chance to reset mCurrentCoro.
|
||||
sCurrentCoro.reset(data->mPrev);
|
||||
// data->mPrev, but this is our last chance to reset Current.
|
||||
Current().reset(data->mPrev);
|
||||
}
|
||||
|
||||
/*****************************************************************************
|
||||
|
|
@ -278,7 +322,7 @@ LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name,
|
|||
mPrev(prev),
|
||||
mName(name),
|
||||
// Wrap the caller's callable in our toplevel() function so we can manage
|
||||
// sCurrentCoro appropriately at startup and shutdown of each coroutine.
|
||||
// Current appropriately at startup and shutdown of each coroutine.
|
||||
mCoro(boost::bind(toplevel, _1, this, callable), stacksize),
|
||||
// don't consume events unless specifically directed
|
||||
mConsuming(false),
|
||||
|
|
@ -289,13 +333,13 @@ LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name,
|
|||
std::string LLCoros::launch(const std::string& prefix, const callable_t& callable)
|
||||
{
|
||||
std::string name(generateDistinctName(prefix));
|
||||
// pass the current value of sCurrentCoro as previous context
|
||||
CoroData* newCoro = new CoroData(sCurrentCoro.get(), name,
|
||||
callable, mStackSize);
|
||||
Current current;
|
||||
// pass the current value of Current as previous context
|
||||
CoroData* newCoro = new CoroData(current, name, callable, mStackSize);
|
||||
// Store it in our pointer map
|
||||
mCoros.insert(name, newCoro);
|
||||
// also set it as current
|
||||
sCurrentCoro.reset(newCoro);
|
||||
current.reset(newCoro);
|
||||
/* Run the coroutine until its first wait, then return here */
|
||||
(newCoro->mCoro)(std::nothrow);
|
||||
return name;
|
||||
|
|
|
|||
|
|
@ -35,8 +35,10 @@
|
|||
#include <boost/ptr_container/ptr_map.hpp>
|
||||
#include <boost/function.hpp>
|
||||
#include <boost/thread/tss.hpp>
|
||||
#include <boost/noncopyable.hpp>
|
||||
#include <string>
|
||||
#include <stdexcept>
|
||||
#include "llcoro_get_id.h" // for friend declaration
|
||||
|
||||
// forward-declare helper class
|
||||
namespace llcoro
|
||||
|
|
@ -83,6 +85,7 @@ class Suspending;
|
|||
*/
|
||||
class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
|
||||
{
|
||||
LLSINGLETON(LLCoros);
|
||||
public:
|
||||
/// Canonical boost::dcoroutines::coroutine signature we use
|
||||
typedef boost::dcoroutines::coroutine<void()> coro;
|
||||
|
|
@ -173,9 +176,8 @@ public:
|
|||
class Future;
|
||||
|
||||
private:
|
||||
LLCoros();
|
||||
friend class LLSingleton<LLCoros>;
|
||||
friend class llcoro::Suspending;
|
||||
friend llcoro::id llcoro::get_id();
|
||||
std::string generateDistinctName(const std::string& prefix) const;
|
||||
bool cleanup(const LLSD&);
|
||||
struct CoroData;
|
||||
|
|
@ -222,8 +224,22 @@ private:
|
|||
typedef boost::ptr_map<std::string, CoroData> CoroMap;
|
||||
CoroMap mCoros;
|
||||
|
||||
// identify the current coroutine's CoroData
|
||||
static boost::thread_specific_ptr<LLCoros::CoroData> sCurrentCoro;
|
||||
// Identify the current coroutine's CoroData. Use a little helper class so
|
||||
// a caller can either use a temporary instance, or instantiate a named
|
||||
// variable and access it multiple times.
|
||||
class Current
|
||||
{
|
||||
public:
|
||||
Current();
|
||||
|
||||
operator LLCoros::CoroData*() { return get(); }
|
||||
LLCoros::CoroData* operator->() { return get(); }
|
||||
LLCoros::CoroData* get() { return mCurrent->get(); }
|
||||
void reset(LLCoros::CoroData* ptr) { mCurrent->reset(ptr); }
|
||||
|
||||
private:
|
||||
boost::thread_specific_ptr<LLCoros::CoroData>* mCurrent;
|
||||
};
|
||||
};
|
||||
|
||||
namespace llcoro
|
||||
|
|
@ -231,7 +247,7 @@ namespace llcoro
|
|||
|
||||
/// Instantiate one of these in a block surrounding any leaf point when
|
||||
/// control literally switches away from this coroutine.
|
||||
class Suspending
|
||||
class Suspending: boost::noncopyable
|
||||
{
|
||||
public:
|
||||
Suspending();
|
||||
|
|
|
|||
|
|
@ -243,6 +243,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
|
||||
|
|
@ -257,31 +265,34 @@ 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::size_type p = name.find(class_prefix);
|
||||
if (p == std::string::npos)
|
||||
std::string name = mangled;
|
||||
if (0 != name.compare(0, class_prefix.length(), class_prefix))
|
||||
{
|
||||
LL_DEBUGS() << "Did not see '" << class_prefix << "' prefix on '"
|
||||
<< name << "'" << LL_ENDL;
|
||||
return name;
|
||||
}
|
||||
|
||||
return name.substr(p + class_prefix.size());
|
||||
return name.substr(class_prefix.length());
|
||||
|
||||
#else
|
||||
return type.name();
|
||||
#else
|
||||
return mangled;
|
||||
#endif
|
||||
}
|
||||
} // LLError
|
||||
|
||||
namespace
|
||||
{
|
||||
std::string functionName(const std::string& preprocessor_name)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
|
|
@ -368,9 +379,8 @@ namespace
|
|||
|
||||
class Globals : public LLSingleton<Globals>
|
||||
{
|
||||
LLSINGLETON(Globals);
|
||||
public:
|
||||
Globals();
|
||||
|
||||
std::ostringstream messageStream;
|
||||
bool messageStreamInUse;
|
||||
|
||||
|
|
@ -443,11 +453,10 @@ namespace LLError
|
|||
|
||||
class Settings : public LLSingleton<Settings>
|
||||
{
|
||||
LLSINGLETON(Settings);
|
||||
public:
|
||||
Settings();
|
||||
|
||||
SettingsConfigPtr getSettingsConfig();
|
||||
|
||||
|
||||
void reset();
|
||||
SettingsStoragePtr saveAndReset();
|
||||
void restore(SettingsStoragePtr pSettingsStorage);
|
||||
|
|
@ -455,7 +464,7 @@ namespace LLError
|
|||
private:
|
||||
SettingsConfigPtr mSettingsConfig;
|
||||
};
|
||||
|
||||
|
||||
SettingsConfig::SettingsConfig()
|
||||
: LLRefCount(),
|
||||
mPrintLocation(false),
|
||||
|
|
@ -480,8 +489,7 @@ namespace LLError
|
|||
mRecorders.clear();
|
||||
}
|
||||
|
||||
Settings::Settings()
|
||||
: LLSingleton<Settings>(),
|
||||
Settings::Settings():
|
||||
mSettingsConfig(new SettingsConfig())
|
||||
{
|
||||
}
|
||||
|
|
@ -490,26 +498,31 @@ namespace LLError
|
|||
{
|
||||
return mSettingsConfig;
|
||||
}
|
||||
|
||||
|
||||
void Settings::reset()
|
||||
{
|
||||
Globals::getInstance()->invalidateCallSites();
|
||||
mSettingsConfig = new SettingsConfig();
|
||||
}
|
||||
|
||||
|
||||
SettingsStoragePtr Settings::saveAndReset()
|
||||
{
|
||||
SettingsStoragePtr oldSettingsConfig(mSettingsConfig.get());
|
||||
reset();
|
||||
return oldSettingsConfig;
|
||||
}
|
||||
|
||||
|
||||
void Settings::restore(SettingsStoragePtr pSettingsStorage)
|
||||
{
|
||||
Globals::getInstance()->invalidateCallSites();
|
||||
SettingsConfigPtr newSettingsConfig(dynamic_cast<SettingsConfig *>(pSettingsStorage.get()));
|
||||
mSettingsConfig = newSettingsConfig;
|
||||
}
|
||||
|
||||
bool is_available()
|
||||
{
|
||||
return Settings::instanceExists() && Globals::instanceExists();
|
||||
}
|
||||
}
|
||||
|
||||
namespace LLError
|
||||
|
|
|
|||
|
|
@ -190,6 +190,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
|
||||
|
|
|
|||
|
|
@ -229,7 +229,7 @@ class LLEventPump;
|
|||
*/
|
||||
class LL_COMMON_API LLEventPumps: public LLSingleton<LLEventPumps>
|
||||
{
|
||||
friend class LLSingleton<LLEventPumps>;
|
||||
LLSINGLETON(LLEventPumps);
|
||||
public:
|
||||
/**
|
||||
* Find or create an LLEventPump instance with a specific name. We return
|
||||
|
|
@ -272,7 +272,6 @@ private:
|
|||
void unregister(const LLEventPump&);
|
||||
|
||||
private:
|
||||
LLEventPumps();
|
||||
~LLEventPumps();
|
||||
|
||||
testable:
|
||||
|
|
|
|||
|
|
@ -239,7 +239,7 @@ int LLFile::close(LLFILE * file)
|
|||
}
|
||||
|
||||
|
||||
int LLFile::remove(const std::string& filename)
|
||||
int LLFile::remove(const std::string& filename, int supress_error)
|
||||
{
|
||||
#if LL_WINDOWS
|
||||
std::string utf8filename = filename;
|
||||
|
|
@ -248,7 +248,7 @@ int LLFile::remove(const std::string& filename)
|
|||
#else
|
||||
int rc = ::remove(filename.c_str());
|
||||
#endif
|
||||
return warnif("remove", filename, rc);
|
||||
return warnif("remove", filename, rc, supress_error);
|
||||
}
|
||||
|
||||
int LLFile::rename(const std::string& filename, const std::string& newname)
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@ public:
|
|||
static int mkdir(const std::string& filename, int perms = 0700);
|
||||
|
||||
static int rmdir(const std::string& filename);
|
||||
static int remove(const std::string& filename);
|
||||
static int remove(const std::string& filename, int supress_error = 0);
|
||||
static int rename(const std::string& filename,const std::string& newname);
|
||||
static bool copy(const std::string from, const std::string to);
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* @file llheteromap.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2016-10-12
|
||||
* @brief Implementation for llheteromap.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2016&license=viewerlgpl$
|
||||
* Copyright (c) 2016, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Precompiled header
|
||||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "llheteromap.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
// other Linden headers
|
||||
|
||||
LLHeteroMap::~LLHeteroMap()
|
||||
{
|
||||
// For each entry in our map, we must call its deleter, which is the only
|
||||
// record we have of its original type.
|
||||
for (TypeMap::iterator mi(mMap.begin()), me(mMap.end()); mi != me; ++mi)
|
||||
{
|
||||
// mi->second is the std::pair; mi->second.first is the void*;
|
||||
// mi->second.second points to the deleter function
|
||||
(mi->second.second)(mi->second.first);
|
||||
mi->second.first = NULL;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* @file llheteromap.h
|
||||
* @author Nat Goodspeed
|
||||
* @date 2016-10-12
|
||||
* @brief Map capable of storing objects of diverse types, looked up by type.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2016&license=viewerlgpl$
|
||||
* Copyright (c) 2016, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
#if ! defined(LL_LLHETEROMAP_H)
|
||||
#define LL_LLHETEROMAP_H
|
||||
|
||||
#include <typeinfo>
|
||||
#include <utility> // std::pair
|
||||
#include <map>
|
||||
|
||||
/**
|
||||
* LLHeteroMap addresses an odd requirement. Usually when you want to put
|
||||
* objects of different classes into a runtime collection of any kind, you
|
||||
* derive them all from a common base class and store pointers to that common
|
||||
* base class.
|
||||
*
|
||||
* LLInitParam::BaseBlock uses disturbing raw-pointer arithmetic to find data
|
||||
* members in its subclasses. It seems that no BaseBlock subclass can be
|
||||
* stored in a polymorphic class of any kind: the presence of a vtbl pointer
|
||||
* in the layout silently throws off the reinterpret_cast arithmetic. Bad
|
||||
* Things result. (Many thanks to Nicky D for this analysis!)
|
||||
*
|
||||
* LLHeteroMap collects objects WITHOUT a common base class, retrieves them by
|
||||
* object type and destroys them when the LLHeteroMap is destroyed.
|
||||
*/
|
||||
class LLHeteroMap
|
||||
{
|
||||
public:
|
||||
~LLHeteroMap();
|
||||
|
||||
/// find or create
|
||||
template <class T>
|
||||
T& obtain()
|
||||
{
|
||||
// Look up map entry by typeid(T). We don't simply use mMap[typeid(T)]
|
||||
// because that requires default-constructing T on every lookup. For
|
||||
// some kinds of T, that could be expensive.
|
||||
TypeMap::iterator found = mMap.find(&typeid(T));
|
||||
if (found == mMap.end())
|
||||
{
|
||||
// Didn't find typeid(T). Create an entry. Because we're storing
|
||||
// only a void* in the map, discarding type information, make sure
|
||||
// we capture that type information in our deleter.
|
||||
void* ptr = new T();
|
||||
void (*dlfn)(void*) = &deleter<T>;
|
||||
std::pair<TypeMap::iterator, bool> inserted =
|
||||
mMap.insert(TypeMap::value_type(&typeid(T),
|
||||
TypeMap::mapped_type(ptr, dlfn)));
|
||||
// Okay, now that we have an entry, claim we found it.
|
||||
found = inserted.first;
|
||||
}
|
||||
// found->second is the std::pair; second.first is the void*
|
||||
// pointer to the object in question. Cast it to correct type and
|
||||
// dereference it.
|
||||
return *(static_cast<T*>(found->second.first));
|
||||
}
|
||||
|
||||
private:
|
||||
template <class T>
|
||||
static
|
||||
void deleter(void* p)
|
||||
{
|
||||
delete static_cast<T*>(p);
|
||||
}
|
||||
|
||||
// Comparing two std::type_info* values is tricky, because the standard
|
||||
// does not guarantee that there will be only one type_info instance for a
|
||||
// given type. In other words, &typeid(A) in one part of the program may
|
||||
// not always equal &typeid(A) in some other part. Use special comparator.
|
||||
struct type_info_ptr_comp
|
||||
{
|
||||
bool operator()(const std::type_info* lhs, const std::type_info* rhs)
|
||||
{
|
||||
return lhs->before(*rhs);
|
||||
}
|
||||
};
|
||||
|
||||
// What we actually store is a map from std::type_info (permitting lookup
|
||||
// by object type) to a void* pointer to the object PLUS its deleter.
|
||||
typedef std::map<
|
||||
const std::type_info*, std::pair<void*, void (*)(void*)>,
|
||||
type_info_ptr_comp>
|
||||
TypeMap;
|
||||
TypeMap mMap;
|
||||
};
|
||||
|
||||
#endif /* ! defined(LL_LLHETEROMAP_H) */
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
/**
|
||||
* @file llinitdestroyclass.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2016-08-30
|
||||
* @brief Implementation for llinitdestroyclass.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2016&license=viewerlgpl$
|
||||
* Copyright (c) 2016, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Precompiled header
|
||||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "llinitdestroyclass.h"
|
||||
// STL headers
|
||||
// std headers
|
||||
// external library headers
|
||||
// other Linden headers
|
||||
#include "llerror.h"
|
||||
|
||||
void LLCallbackRegistry::fireCallbacks() const
|
||||
{
|
||||
for (FuncList::const_iterator fi = mCallbacks.begin(), fe = mCallbacks.end();
|
||||
fi != fe; ++fi)
|
||||
{
|
||||
LL_INFOS("LLInitDestroyClass") << "calling " << fi->first << "()" << LL_ENDL;
|
||||
fi->second();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,175 @@
|
|||
/**
|
||||
* @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 "llsingleton.h"
|
||||
#include <boost/function.hpp>
|
||||
#include <typeinfo>
|
||||
#include <vector>
|
||||
#include <utility> // std::pair
|
||||
|
||||
/**
|
||||
* LLCallbackRegistry is an implementation detail base class for
|
||||
* LLInitClassList and LLDestroyClassList. It accumulates the initClass() or
|
||||
* destroyClass() callbacks for registered classes.
|
||||
*/
|
||||
class LLCallbackRegistry
|
||||
{
|
||||
public:
|
||||
typedef boost::function<void()> func_t;
|
||||
|
||||
void registerCallback(const std::string& name, const func_t& func)
|
||||
{
|
||||
mCallbacks.push_back(FuncList::value_type(name, func));
|
||||
}
|
||||
|
||||
void fireCallbacks() const;
|
||||
|
||||
private:
|
||||
// Arguably this should be a boost::signals2::signal, which is, after all,
|
||||
// a sequence of callables. We manage it by hand so we can log a name for
|
||||
// each registered function we call.
|
||||
typedef std::vector< std::pair<std::string, func_t> > FuncList;
|
||||
FuncList 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>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(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>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(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(const std::string& name, const LLCallbackRegistry::func_t& func)
|
||||
{
|
||||
T::instance().registerCallback(name, 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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;
|
||||
};
|
||||
|
||||
// Here's where LLInitClass<T> specifies the subclass initClass() method.
|
||||
template <typename T>
|
||||
LLRegisterWith<LLInitClassList>
|
||||
LLInitClass<T>::sRegister(std::string(typeid(T).name()) + "::initClass",
|
||||
&T::initClass);
|
||||
// Here's where LLDestroyClass<T> specifies the subclass destroyClass() method.
|
||||
template <typename T>
|
||||
LLRegisterWith<LLDestroyClassList>
|
||||
LLDestroyClass<T>::sRegister(std::string(typeid(T).name()) + "::destroyClass",
|
||||
&T::destroyClass);
|
||||
|
||||
#endif /* ! defined(LL_LLINITDESTROYCLASS_H) */
|
||||
|
|
@ -193,7 +193,12 @@ namespace LLInitParam
|
|||
{
|
||||
if (!silent)
|
||||
{
|
||||
p.parserWarning(llformat("Failed to parse parameter \"%s\"", p.getCurrentElementName().c_str()));
|
||||
std::string file_name = p.getCurrentFileName();
|
||||
if(!file_name.empty())
|
||||
{
|
||||
file_name = "in file: " + file_name;
|
||||
}
|
||||
p.parserWarning(llformat("Failed to parse parameter \"%s\" %s", p.getCurrentElementName().c_str(), file_name.c_str()));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -551,6 +551,7 @@ namespace LLInitParam
|
|||
}
|
||||
|
||||
virtual std::string getCurrentElementName() = 0;
|
||||
virtual std::string getCurrentFileName() = 0;
|
||||
virtual void parserWarning(const std::string& message);
|
||||
virtual void parserError(const std::string& message);
|
||||
void setParseSilently(bool silent) { mParseSilently = silent; }
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
LLMetricPerformanceTesterBasic::name_tester_map_t LLMetricPerformanceTesterBasic::sTesterMap ;
|
||||
|
||||
/*static*/
|
||||
void LLMetricPerformanceTesterBasic::cleanClass()
|
||||
void LLMetricPerformanceTesterBasic::cleanupClass()
|
||||
{
|
||||
for (name_tester_map_t::iterator iter = sTesterMap.begin() ; iter != sTesterMap.end() ; ++iter)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -156,7 +156,7 @@ public:
|
|||
/**
|
||||
* @brief Delete all testers and reset the tester map
|
||||
*/
|
||||
static void cleanClass() ;
|
||||
static void cleanupClass() ;
|
||||
|
||||
private:
|
||||
// Add a tester to the map. Returns false if adding fails.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,216 @@
|
|||
/**
|
||||
* @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> >
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLPounceableQueueSingleton);
|
||||
|
||||
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) */
|
||||
|
|
@ -247,7 +247,10 @@ class LLRegistrySingleton
|
|||
: public LLRegistry<KEY, VALUE, COMPARATOR>,
|
||||
public LLSingleton<DERIVED_TYPE>
|
||||
{
|
||||
friend class LLSingleton<DERIVED_TYPE>;
|
||||
// This LLRegistrySingleton doesn't use LLSINGLETON(LLRegistrySingleton)
|
||||
// because the concrete class is actually DERIVED_TYPE, not
|
||||
// LLRegistrySingleton. So each concrete subclass needs
|
||||
// LLSINGLETON(whatever) -- not this intermediate base class.
|
||||
public:
|
||||
typedef LLRegistry<KEY, VALUE, COMPARATOR> registry_t;
|
||||
typedef const KEY& ref_const_key_t;
|
||||
|
|
@ -269,7 +272,7 @@ public:
|
|||
|
||||
~ScopedRegistrar()
|
||||
{
|
||||
if (!singleton_t::destroyed())
|
||||
if (singleton_t::instanceExists())
|
||||
{
|
||||
popScope();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ public:
|
|||
}
|
||||
|
||||
/*virtual*/ std::string getCurrentElementName();
|
||||
/*virtual*/ std::string getCurrentFileName(){ return LLStringUtil::null; }
|
||||
|
||||
private:
|
||||
void writeSDImpl(LLSD& sd,
|
||||
|
|
|
|||
|
|
@ -24,9 +24,446 @@
|
|||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// <FS:Ansariel> Get rid of LNK4221 linker warning since we don't run the unit tests anyway
|
||||
//#include "linden_common.h"
|
||||
#include "linden_common.h"
|
||||
#include "llsingleton.h"
|
||||
|
||||
//#include "llsingleton.h"
|
||||
#include "llerror.h"
|
||||
#include "llerrorcontrol.h" // LLError::is_available()
|
||||
#include "lldependencies.h"
|
||||
#include "llcoro_get_id.h"
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/unordered_map.hpp>
|
||||
#include <algorithm>
|
||||
#include <iostream> // std::cerr in dire emergency
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace {
|
||||
void log(LLError::ELevel level,
|
||||
const char* p1, const char* p2, const char* p3, const char* p4);
|
||||
|
||||
void logdebugs(const char* p1="", const char* p2="", const char* p3="", const char* p4="");
|
||||
|
||||
bool oktolog();
|
||||
} // anonymous namespace
|
||||
|
||||
// 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>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(MasterList);
|
||||
|
||||
public:
|
||||
// No need to make this private with accessors; nobody outside this source
|
||||
// file can see it.
|
||||
|
||||
// This is the master list of all instantiated LLSingletons (save the
|
||||
// MasterList itself) in arbitrary order. You MUST call dep_sort() before
|
||||
// traversing this list.
|
||||
LLSingletonBase::list_t mMaster;
|
||||
|
||||
// We need to maintain a stack of LLSingletons currently being
|
||||
// initialized, either in the constructor or in initSingleton(). However,
|
||||
// managing that as a stack depends on having a DISTINCT 'initializing'
|
||||
// stack for every C++ stack in the process! And we have a distinct C++
|
||||
// stack for every running coroutine. It would be interesting and cool to
|
||||
// implement a generic coroutine-local-storage mechanism and use that
|
||||
// here. The trouble is that LLCoros is itself an LLSingleton, so
|
||||
// depending on LLCoros functionality could dig us into infinite
|
||||
// recursion. (Moreover, when we reimplement LLCoros on top of
|
||||
// Boost.Fiber, that library already provides fiber_specific_ptr -- so
|
||||
// it's not worth a great deal of time and energy implementing a generic
|
||||
// equivalent on top of boost::dcoroutine, which is on its way out.)
|
||||
// Instead, use a map of llcoro::id to select the appropriate
|
||||
// coro-specific 'initializing' stack. llcoro::get_id() is carefully
|
||||
// implemented to avoid requiring LLCoros.
|
||||
typedef boost::unordered_map<llcoro::id, LLSingletonBase::list_t> InitializingMap;
|
||||
InitializingMap mInitializing;
|
||||
|
||||
// non-static method, cf. LLSingletonBase::get_initializing()
|
||||
list_t& get_initializing_()
|
||||
{
|
||||
// map::operator[] has find-or-create semantics, exactly what we need
|
||||
// here. It returns a reference to the selected mapped_type instance.
|
||||
return mInitializing[llcoro::get_id()];
|
||||
}
|
||||
|
||||
void cleanup_initializing_()
|
||||
{
|
||||
InitializingMap::iterator found = mInitializing.find(llcoro::get_id());
|
||||
if (found != mInitializing.end())
|
||||
{
|
||||
mInitializing.erase(found);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//static
|
||||
LLSingletonBase::list_t& LLSingletonBase::get_master()
|
||||
{
|
||||
return LLSingletonBase::MasterList::instance().mMaster;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
//static
|
||||
LLSingletonBase::list_t& LLSingletonBase::get_initializing()
|
||||
{
|
||||
return LLSingletonBase::MasterList::instance().get_initializing_();
|
||||
}
|
||||
|
||||
//static
|
||||
LLSingletonBase::list_t& LLSingletonBase::get_initializing_from(MasterList* master)
|
||||
{
|
||||
return master->get_initializing_();
|
||||
}
|
||||
|
||||
LLSingletonBase::~LLSingletonBase() {}
|
||||
|
||||
void LLSingletonBase::push_initializing(const char* name)
|
||||
{
|
||||
// log BEFORE pushing so logging singletons don't cry circularity
|
||||
log_initializing("Pushing", name);
|
||||
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()");
|
||||
}
|
||||
|
||||
// Now we know list.back() exists: capture it
|
||||
LLSingletonBase* back(list.back());
|
||||
// and pop it
|
||||
list.pop_back();
|
||||
|
||||
// The viewer launches an open-ended number of coroutines. While we don't
|
||||
// expect most of them to initialize LLSingleton instances, our present
|
||||
// get_initializing() logic could lead to an open-ended number of map
|
||||
// entries. So every time we pop the stack back to empty, delete the entry
|
||||
// entirely.
|
||||
if (list.empty())
|
||||
{
|
||||
MasterList::instance().cleanup_initializing_();
|
||||
}
|
||||
|
||||
// Now validate the newly-popped LLSingleton.
|
||||
if (back != this)
|
||||
{
|
||||
logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ",
|
||||
demangle(typeid(*this).name()).c_str(), "::getInstance() trying to pop ",
|
||||
demangle(typeid(*back).name()).c_str());
|
||||
}
|
||||
|
||||
// log AFTER popping so logging singletons don't cry circularity
|
||||
log_initializing("Popping", typeid(*back).name());
|
||||
}
|
||||
|
||||
//static
|
||||
void LLSingletonBase::log_initializing(const char* verb, const char* name)
|
||||
{
|
||||
if (oktolog())
|
||||
{
|
||||
LL_DEBUGS("LLSingleton") << verb << ' ' << demangle(name) << ';';
|
||||
list_t& list(get_initializing());
|
||||
for (list_t::const_reverse_iterator ri(list.rbegin()), rend(list.rend());
|
||||
ri != rend; ++ri)
|
||||
{
|
||||
LLSingletonBase* sb(*ri);
|
||||
LL_CONT << ' ' << demangle(typeid(*sb).name());
|
||||
}
|
||||
LL_ENDL;
|
||||
}
|
||||
}
|
||||
|
||||
void LLSingletonBase::capture_dependency(list_t& initializing, 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().
|
||||
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.
|
||||
LLSingletonBase* foundp(*found);
|
||||
out << demangle(typeid(*foundp).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.
|
||||
LLSingletonBase* current(initializing.back());
|
||||
if (current->mDepends.insert(this).second)
|
||||
{
|
||||
// only log the FIRST time we hit this dependency!
|
||||
logdebugs(demangle(typeid(*current).name()).c_str(),
|
||||
" depends on ", demangle(typeid(*this).name()).c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//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;
|
||||
|
||||
logdebugs("calling ",
|
||||
demangle(typeid(*sp).name()).c_str(), "::cleanupSingleton()");
|
||||
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.
|
||||
logdebugs("calling ", name.c_str(), "::deleteSingleton()");
|
||||
// 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 -----------------------------*/
|
||||
namespace {
|
||||
bool oktolog()
|
||||
{
|
||||
// See comments in log() below.
|
||||
return sMasterRefcount.refcount && LLError::is_available();
|
||||
}
|
||||
|
||||
void log(LLError::ELevel level,
|
||||
const char* p1, const char* p2, const char* p3, const char* p4)
|
||||
{
|
||||
// Check whether we're in the implicit final LLSingletonBase::deleteAll()
|
||||
// call. We've carefully arranged for deleteAll() to be called when the
|
||||
// last SingletonLifetimeManager instance is destroyed -- in other words,
|
||||
// when the last translation unit containing an LLSingleton instance
|
||||
// cleans up static data. That could happen after std::cerr is destroyed!
|
||||
// The is_available() test below ensures that we'll stop logging once
|
||||
// LLError has been cleaned up. If we had a similar portable test for
|
||||
// std::cerr, this would be a good place to use it. As we do not, just
|
||||
// don't log anything during implicit final deleteAll(). Detect that by
|
||||
// the master refcount having gone to zero.
|
||||
if (sMasterRefcount.refcount == 0)
|
||||
return;
|
||||
|
||||
// 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_VLOGS(level, "LLSingleton") << 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;
|
||||
}
|
||||
}
|
||||
|
||||
void logdebugs(const char* p1, const char* p2, const char* p3, const char* p4)
|
||||
{
|
||||
log(LLError::LEVEL_DEBUG, p1, p2, p3, p4);
|
||||
}
|
||||
} // anonymous namespace
|
||||
|
||||
//static
|
||||
void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4)
|
||||
{
|
||||
log(LLError::LEVEL_WARN, p1, p2, p3, p4);
|
||||
}
|
||||
|
||||
//static
|
||||
void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4)
|
||||
{
|
||||
log(LLError::LEVEL_ERROR, p1, p2, p3, p4);
|
||||
// The other important side effect of LL_ERRS() is
|
||||
// https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG)
|
||||
LLError::crashAndLoop(std::string());
|
||||
}
|
||||
|
||||
std::string LLSingletonBase::demangle(const char* mangled)
|
||||
{
|
||||
return LLError::Log::demangle(mangled);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,188 +25,495 @@
|
|||
#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();
|
||||
static list_t& get_initializing_from(MasterList*);
|
||||
// Produce a vector<LLSingletonBase*> of master list, in dependency order.
|
||||
typedef std::vector<LLSingletonBase*> vec_t;
|
||||
static vec_t dep_sort();
|
||||
|
||||
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;
|
||||
|
||||
// Define tag<T> to pass to our template constructor. You can't explicitly
|
||||
// invoke a template constructor with ordinary template syntax:
|
||||
// http://stackoverflow.com/a/3960925/5533635
|
||||
template <typename T>
|
||||
struct tag
|
||||
{
|
||||
typedef T type;
|
||||
};
|
||||
|
||||
// Base-class constructor should only be invoked by the DERIVED_TYPE
|
||||
// constructor, which passes tag<DERIVED_TYPE> for various purposes.
|
||||
template <typename DERIVED_TYPE>
|
||||
LLSingletonBase(tag<DERIVED_TYPE>);
|
||||
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(const char*);
|
||||
// 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();
|
||||
private:
|
||||
// logging
|
||||
static void log_initializing(const char* verb, const char* name);
|
||||
protected:
|
||||
// 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(list_t& initializing, 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 real LLSingletonBase methods.
|
||||
template <class T>
|
||||
struct LLSingleton_manage_master
|
||||
{
|
||||
void add(LLSingletonBase* sb) { sb->add_master(); }
|
||||
void remove(LLSingletonBase* sb) { sb->remove_master(); }
|
||||
void push_initializing(LLSingletonBase* sb) { sb->push_initializing(typeid(T).name()); }
|
||||
void pop_initializing (LLSingletonBase* sb) { sb->pop_initializing(); }
|
||||
LLSingletonBase::list_t& get_initializing(T*) { return LLSingletonBase::get_initializing(); }
|
||||
};
|
||||
|
||||
// But for the specific case of LLSingletonBase::MasterList, don't.
|
||||
template <>
|
||||
struct LLSingleton_manage_master<LLSingletonBase::MasterList>
|
||||
{
|
||||
void add(LLSingletonBase*) {}
|
||||
void remove(LLSingletonBase*) {}
|
||||
void push_initializing(LLSingletonBase*) {}
|
||||
void pop_initializing (LLSingletonBase*) {}
|
||||
LLSingletonBase::list_t& get_initializing(LLSingletonBase::MasterList* instance)
|
||||
{
|
||||
return LLSingletonBase::get_initializing_from(instance);
|
||||
}
|
||||
};
|
||||
|
||||
// Now we can implement LLSingletonBase's template constructor.
|
||||
template <typename DERIVED_TYPE>
|
||||
LLSingletonBase::LLSingletonBase(tag<DERIVED_TYPE>):
|
||||
mCleaned(false),
|
||||
mDeleteSingleton(NULL)
|
||||
{
|
||||
// Make this the currently-initializing LLSingleton.
|
||||
LLSingleton_manage_master<DERIVED_TYPE>().push_initializing(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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>
|
||||
* {
|
||||
* // use this macro at start of every LLSingleton subclass
|
||||
* LLSINGLETON(Foo);
|
||||
* public:
|
||||
* // ...
|
||||
* };
|
||||
*
|
||||
* 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;
|
||||
}
|
||||
// We know of no way to instruct the compiler that every subclass
|
||||
// constructor MUST be private. However, we can make the LLSINGLETON()
|
||||
// macro both declare a private constructor and provide the required
|
||||
// friend declaration. How can we ensure that every subclass uses
|
||||
// LLSINGLETON()? By making that macro provide a definition for this pure
|
||||
// virtual method. If you get "can't instantiate class due to missing pure
|
||||
// virtual method" for this method, then add LLSINGLETON(yourclass) in the
|
||||
// subclass body.
|
||||
virtual void you_must_use_LLSINGLETON_macro() = 0;
|
||||
|
||||
// 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:
|
||||
// Pass DERIVED_TYPE explicitly to LLSingletonBase's constructor because,
|
||||
// until our subclass constructor completes, *this isn't yet a
|
||||
// full-fledged DERIVED_TYPE.
|
||||
LLSingleton(): LLSingletonBase(LLSingletonBase::tag<DERIVED_TYPE>())
|
||||
{
|
||||
// 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
|
||||
LLSingleton_manage_master<DERIVED_TYPE>().pop_initializing(sData.mInstance);
|
||||
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
|
||||
LLSingleton_manage_master<DERIVED_TYPE>().pop_initializing(sData.mInstance);
|
||||
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(
|
||||
LLSingleton_manage_master<DERIVED_TYPE>().get_initializing(sData.mInstance),
|
||||
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>
|
||||
typename LLSingleton<T>::SingletonData LLSingleton<T>::sData;
|
||||
|
||||
/**
|
||||
* Use LLSINGLETON(Foo); at the start of an LLSingleton<Foo> subclass body
|
||||
* when you want to declare an out-of-line constructor:
|
||||
*
|
||||
* @code
|
||||
* class Foo: public LLSingleton<Foo>
|
||||
* {
|
||||
* // use this macro at start of every LLSingleton subclass
|
||||
* LLSINGLETON(Foo);
|
||||
* public:
|
||||
* // ...
|
||||
* };
|
||||
* // ...
|
||||
* [inline]
|
||||
* Foo::Foo() { ... }
|
||||
* @endcode
|
||||
*
|
||||
* Unfortunately, this mechanism does not permit you to define even a simple
|
||||
* (but nontrivial) constructor within the class body. If it's literally
|
||||
* trivial, use LLSINGLETON_EMPTY_CTOR(); if not, use LLSINGLETON() and define
|
||||
* the constructor outside the class body. If you must define it in a header
|
||||
* file, use 'inline' (unless it's a template class) to avoid duplicate-symbol
|
||||
* errors at link time.
|
||||
*/
|
||||
#define LLSINGLETON(DERIVED_CLASS) \
|
||||
private: \
|
||||
/* implement LLSingleton pure virtual method whose sole purpose */ \
|
||||
/* is to remind people to use this macro */ \
|
||||
virtual void you_must_use_LLSINGLETON_macro() {} \
|
||||
friend class LLSingleton<DERIVED_CLASS>; \
|
||||
DERIVED_CLASS()
|
||||
|
||||
/**
|
||||
* Use LLSINGLETON_EMPTY_CTOR(Foo); at the start of an LLSingleton<Foo>
|
||||
* subclass body when the constructor is trivial:
|
||||
*
|
||||
* @code
|
||||
* class Foo: public LLSingleton<Foo>
|
||||
* {
|
||||
* // use this macro at start of every LLSingleton subclass
|
||||
* LLSINGLETON_EMPTY_CTOR(Foo);
|
||||
* public:
|
||||
* // ...
|
||||
* };
|
||||
* @endcode
|
||||
*/
|
||||
#define LLSINGLETON_EMPTY_CTOR(DERIVED_CLASS) \
|
||||
/* LLSINGLETON() is carefully implemented to permit exactly this */ \
|
||||
LLSINGLETON(DERIVED_CLASS) {}
|
||||
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -0,0 +1,163 @@
|
|||
/**
|
||||
* @file llheteromap_test.cpp
|
||||
* @author Nat Goodspeed
|
||||
* @date 2016-10-12
|
||||
* @brief Test for llheteromap.
|
||||
*
|
||||
* $LicenseInfo:firstyear=2016&license=viewerlgpl$
|
||||
* Copyright (c) 2016, Linden Research, Inc.
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
// Precompiled header
|
||||
#include "linden_common.h"
|
||||
// associated header
|
||||
#include "llheteromap.h"
|
||||
// STL headers
|
||||
#include <set>
|
||||
// std headers
|
||||
// external library headers
|
||||
|
||||
// (pacify clang)
|
||||
std::ostream& operator<<(std::ostream& out, const std::set<std::string>& strset);
|
||||
// other Linden headers
|
||||
#include "../test/lltut.h"
|
||||
|
||||
static std::string clog;
|
||||
static std::set<std::string> dlog;
|
||||
|
||||
// want to be able to use ensure_equals() on a set<string>
|
||||
std::ostream& operator<<(std::ostream& out, const std::set<std::string>& strset)
|
||||
{
|
||||
out << '{';
|
||||
const char* delim = "";
|
||||
for (std::set<std::string>::const_iterator si(strset.begin()), se(strset.end());
|
||||
si != se; ++si)
|
||||
{
|
||||
out << delim << '"' << *si << '"';
|
||||
delim = ", ";
|
||||
}
|
||||
out << '}';
|
||||
return out;
|
||||
}
|
||||
|
||||
// unrelated test classes
|
||||
struct Chalk
|
||||
{
|
||||
int dummy;
|
||||
std::string name;
|
||||
|
||||
Chalk():
|
||||
dummy(0)
|
||||
{
|
||||
clog.append("a");
|
||||
}
|
||||
|
||||
~Chalk()
|
||||
{
|
||||
dlog.insert("a");
|
||||
}
|
||||
|
||||
private:
|
||||
Chalk(const Chalk&); // no implementation
|
||||
};
|
||||
|
||||
struct Cheese
|
||||
{
|
||||
std::string name;
|
||||
|
||||
Cheese()
|
||||
{
|
||||
clog.append("e");
|
||||
}
|
||||
|
||||
~Cheese()
|
||||
{
|
||||
dlog.insert("e");
|
||||
}
|
||||
|
||||
private:
|
||||
Cheese(const Cheese&); // no implementation
|
||||
};
|
||||
|
||||
struct Chowdah
|
||||
{
|
||||
char displace[17];
|
||||
std::string name;
|
||||
|
||||
Chowdah()
|
||||
{
|
||||
displace[0] = '\0';
|
||||
clog.append("o");
|
||||
}
|
||||
|
||||
~Chowdah()
|
||||
{
|
||||
dlog.insert("o");
|
||||
}
|
||||
|
||||
private:
|
||||
Chowdah(const Chowdah&); // no implementation
|
||||
};
|
||||
|
||||
/*****************************************************************************
|
||||
* TUT
|
||||
*****************************************************************************/
|
||||
namespace tut
|
||||
{
|
||||
struct llheteromap_data
|
||||
{
|
||||
llheteromap_data()
|
||||
{
|
||||
clog.erase();
|
||||
dlog.clear();
|
||||
}
|
||||
};
|
||||
typedef test_group<llheteromap_data> llheteromap_group;
|
||||
typedef llheteromap_group::object object;
|
||||
llheteromap_group llheteromapgrp("llheteromap");
|
||||
|
||||
template<> template<>
|
||||
void object::test<1>()
|
||||
{
|
||||
set_test_name("create, get, delete");
|
||||
|
||||
{
|
||||
LLHeteroMap map;
|
||||
|
||||
{
|
||||
// create each instance
|
||||
Chalk& chalk = map.obtain<Chalk>();
|
||||
chalk.name = "Chalk";
|
||||
|
||||
Cheese& cheese = map.obtain<Cheese>();
|
||||
cheese.name = "Cheese";
|
||||
|
||||
Chowdah& chowdah = map.obtain<Chowdah>();
|
||||
chowdah.name = "Chowdah";
|
||||
} // refs go out of scope
|
||||
|
||||
{
|
||||
// verify each instance
|
||||
Chalk& chalk = map.obtain<Chalk>();
|
||||
ensure_equals(chalk.name, "Chalk");
|
||||
|
||||
Cheese& cheese = map.obtain<Cheese>();
|
||||
ensure_equals(cheese.name, "Cheese");
|
||||
|
||||
Chowdah& chowdah = map.obtain<Chowdah>();
|
||||
ensure_equals(chowdah.name, "Chowdah");
|
||||
}
|
||||
} // destroy map
|
||||
|
||||
// Chalk, Cheese and Chowdah should have been created in specific order
|
||||
ensure_equals(clog, "aeo");
|
||||
|
||||
// We don't care what order they're destroyed in, as long as each is
|
||||
// appropriately destroyed.
|
||||
std::set<std::string> dtorset;
|
||||
for (const char* cp = "aeo"; *cp; ++cp)
|
||||
dtorset.insert(std::string(1, *cp));
|
||||
ensure_equals(dlog, dtorset);
|
||||
}
|
||||
} // namespace tut
|
||||
|
|
@ -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,172 @@
|
|||
#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> \
|
||||
{ \
|
||||
LLSINGLETON(CLS); \
|
||||
~CLS(); \
|
||||
public: \
|
||||
static enum dep_flag { \
|
||||
DEP_NONE, /* no dependency */ \
|
||||
DEP_CTOR, /* dependency in ctor */ \
|
||||
DEP_INIT /* dependency in initSingleton */ \
|
||||
} sDepFlag; \
|
||||
\
|
||||
void initSingleton(); \
|
||||
void cleanupSingleton(); \
|
||||
}; \
|
||||
\
|
||||
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>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(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<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<3>()
|
||||
{
|
||||
//Construct the instance
|
||||
LLSingletonTest::getInstance();
|
||||
ensure(LLSingletonTest::instanceExists());
|
||||
|
||||
//Delete the instance
|
||||
LLSingletonTest::deleteSingleton();
|
||||
ensure(LLSingletonTest::destroyed());
|
||||
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());
|
||||
}
|
||||
//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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ target_link_libraries(
|
|||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
)
|
||||
|
||||
# tests
|
||||
|
|
@ -133,8 +134,8 @@ if (LL_TESTS)
|
|||
${CURL_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
)
|
||||
|
||||
# If http_proxy is in the current environment (e.g. to fetch s3-proxy
|
||||
|
|
@ -201,8 +202,8 @@ endif (DARWIN)
|
|||
${CURL_LIBRARIES}
|
||||
${OPENSSL_LIBRARIES}
|
||||
${CRYPTO_LIBRARIES}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
${BOOST_THREAD_LIBRARY}
|
||||
${BOOST_SYSTEM_LIBRARY}
|
||||
)
|
||||
|
||||
add_executable(http_texture_load
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -729,7 +729,7 @@ void HttpRequestTestObjectType::test<7>()
|
|||
#if 0 // defined(WIN32)
|
||||
// Can't do this on any platform anymore, the LL logging system holds
|
||||
// on to memory and produces what looks like memory leaks...
|
||||
|
||||
|
||||
// printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
|
||||
ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
|
||||
#endif
|
||||
|
|
@ -1459,21 +1459,21 @@ void HttpRequestTestObjectType::test<14>()
|
|||
// references to it after completion of this method.
|
||||
// Create before memory record as the string copy will bump numbers.
|
||||
TestHandler2 handler(this, "handler");
|
||||
LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
|
||||
std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep
|
||||
|
||||
LLCore::HttpHandler::ptr_t handlerp(&handler, NoOpDeletor);
|
||||
std::string url_base(get_base_url() + "/sleep/"); // path to a 30-second sleep
|
||||
|
||||
// record the total amount of dynamically allocated memory
|
||||
mMemTotal = GetMemTotal();
|
||||
mHandlerCalls = 0;
|
||||
|
||||
HttpRequest * req = NULL;
|
||||
HttpOptions::ptr_t opts;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
// Get singletons created
|
||||
// Get singletons created
|
||||
HttpRequest::createService();
|
||||
|
||||
|
||||
// Start threading early so that thread memory is invariant
|
||||
// over the test.
|
||||
HttpRequest::startThread();
|
||||
|
|
@ -1482,10 +1482,10 @@ void HttpRequestTestObjectType::test<14>()
|
|||
req = new HttpRequest();
|
||||
ensure("Memory allocated on construction", mMemTotal < GetMemTotal());
|
||||
|
||||
opts = HttpOptions::ptr_t(new HttpOptions);
|
||||
opts->setRetries(0); // Don't retry
|
||||
opts = HttpOptions::ptr_t(new HttpOptions);
|
||||
opts->setRetries(0); // Don't retry
|
||||
opts->setTimeout(2);
|
||||
|
||||
|
||||
// Issue a GET that sleeps
|
||||
mStatus = HttpStatus(HttpStatus::EXT_CURL_EASY, CURLE_OPERATION_TIMEDOUT);
|
||||
HttpHandle handle = req->requestGetByteRange(HttpRequest::DEFAULT_POLICY_ID,
|
||||
|
|
@ -1494,8 +1494,8 @@ void HttpRequestTestObjectType::test<14>()
|
|||
0,
|
||||
0,
|
||||
opts,
|
||||
HttpHeaders::ptr_t(),
|
||||
handlerp);
|
||||
HttpHeaders::ptr_t(),
|
||||
handlerp);
|
||||
ensure("Valid handle returned for ranged request", handle != LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
// Run the notification pump.
|
||||
|
|
@ -1513,7 +1513,7 @@ void HttpRequestTestObjectType::test<14>()
|
|||
mStatus = HttpStatus();
|
||||
handle = req->requestStopThread(handlerp);
|
||||
ensure("Valid handle returned for second request", handle != LLCORE_HTTP_HANDLE_INVALID);
|
||||
|
||||
|
||||
// Run the notification pump again
|
||||
count = 0;
|
||||
limit = LOOP_COUNT_LONG;
|
||||
|
|
@ -1535,30 +1535,29 @@ void HttpRequestTestObjectType::test<14>()
|
|||
ensure("Thread actually stopped running", HttpService::isStopped());
|
||||
|
||||
// release options
|
||||
opts.reset();
|
||||
|
||||
opts.reset();
|
||||
|
||||
// release the request object
|
||||
delete req;
|
||||
req = NULL;
|
||||
|
||||
// Shut down service
|
||||
HttpRequest::destroyService();
|
||||
|
||||
|
||||
ensure("Two handler calls on the way out", 2 == mHandlerCalls);
|
||||
|
||||
#if defined(WIN32)
|
||||
// Can only do this memory test on Windows. On other platforms,
|
||||
// the LL logging system holds on to memory and produces what looks
|
||||
// like memory leaks...
|
||||
|
||||
// printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
|
||||
#if 0 // defined(WIN32)
|
||||
// Can't do this on any platform anymore, the LL logging system holds
|
||||
// on to memory and produces what looks like memory leaks...
|
||||
|
||||
// printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
|
||||
ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
|
||||
#endif
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
stop_thread(req);
|
||||
opts.reset();
|
||||
opts.reset();
|
||||
delete req;
|
||||
HttpRequest::destroyService();
|
||||
throw;
|
||||
|
|
@ -3065,12 +3064,11 @@ void HttpRequestTestObjectType::test<22>()
|
|||
|
||||
// Shut down service
|
||||
HttpRequest::destroyService();
|
||||
|
||||
#if defined(WIN32)
|
||||
// Can only do this memory test on Windows. On other platforms,
|
||||
// the LL logging system holds on to memory and produces what looks
|
||||
// like memory leaks...
|
||||
|
||||
|
||||
#if 0 // defined(WIN32)
|
||||
// Can't do this on any platform anymore, the LL logging system holds
|
||||
// on to memory and produces what looks like memory leaks...
|
||||
|
||||
// printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
|
||||
ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
|
||||
#endif
|
||||
|
|
@ -3195,12 +3193,11 @@ void HttpRequestTestObjectType::test<23>()
|
|||
|
||||
// Shut down service
|
||||
HttpRequest::destroyService();
|
||||
|
||||
#if defined(WIN32)
|
||||
// Can only do this memory test on Windows. On other platforms,
|
||||
// the LL logging system holds on to memory and produces what looks
|
||||
// like memory leaks...
|
||||
|
||||
|
||||
#if 0 // defined(WIN32)
|
||||
// Can't do this on any platform anymore, the LL logging system holds
|
||||
// on to memory and produces what looks like memory leaks...
|
||||
|
||||
// printf("Old mem: %d, New mem: %d\n", mMemTotal, GetMemTotal());
|
||||
ensure("Memory usage back to that at entry", mMemTotal == GetMemTotal());
|
||||
#endif
|
||||
|
|
|
|||
|
|
@ -45,6 +45,7 @@
|
|||
#include "llhttpsdhandler.h"
|
||||
#include "httpcommon.h"
|
||||
#include "httpresponse.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <openssl/crypto.h>
|
||||
|
|
@ -779,7 +780,7 @@ void LLCrashLogger::commonCleanup()
|
|||
{
|
||||
term_curl();
|
||||
LLError::logToFile(""); //close crashreport.log
|
||||
LLProxy::cleanupClass();
|
||||
SUBSYSTEM_CLEANUP(LLProxy);
|
||||
}
|
||||
|
||||
void LLCrashLogger::init_curl()
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@
|
|||
#include "v3math.h"
|
||||
|
||||
|
||||
LLGlobalEconomy::LLGlobalEconomy()
|
||||
LLBaseEconomy::LLBaseEconomy()
|
||||
: mObjectCount( -1 ),
|
||||
mObjectCapacity( -1 ),
|
||||
mPriceObjectClaim( -1 ),
|
||||
|
|
@ -45,15 +45,15 @@ LLGlobalEconomy::LLGlobalEconomy()
|
|||
mPriceGroupCreate( -1 )
|
||||
{ }
|
||||
|
||||
LLGlobalEconomy::~LLGlobalEconomy()
|
||||
LLBaseEconomy::~LLBaseEconomy()
|
||||
{ }
|
||||
|
||||
void LLGlobalEconomy::addObserver(LLEconomyObserver* observer)
|
||||
void LLBaseEconomy::addObserver(LLEconomyObserver* observer)
|
||||
{
|
||||
mObservers.push_back(observer);
|
||||
}
|
||||
|
||||
void LLGlobalEconomy::removeObserver(LLEconomyObserver* observer)
|
||||
void LLBaseEconomy::removeObserver(LLEconomyObserver* observer)
|
||||
{
|
||||
std::list<LLEconomyObserver*>::iterator it =
|
||||
std::find(mObservers.begin(), mObservers.end(), observer);
|
||||
|
|
@ -63,7 +63,7 @@ void LLGlobalEconomy::removeObserver(LLEconomyObserver* observer)
|
|||
}
|
||||
}
|
||||
|
||||
void LLGlobalEconomy::notifyObservers()
|
||||
void LLBaseEconomy::notifyObservers()
|
||||
{
|
||||
for (std::list<LLEconomyObserver*>::iterator it = mObservers.begin();
|
||||
it != mObservers.end();
|
||||
|
|
@ -74,7 +74,7 @@ void LLGlobalEconomy::notifyObservers()
|
|||
}
|
||||
|
||||
// static
|
||||
void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data)
|
||||
void LLBaseEconomy::processEconomyData(LLMessageSystem *msg, LLBaseEconomy* econ_data)
|
||||
{
|
||||
S32 i;
|
||||
F32 f;
|
||||
|
|
@ -117,7 +117,7 @@ void LLGlobalEconomy::processEconomyData(LLMessageSystem *msg, LLGlobalEconomy*
|
|||
econ_data->notifyObservers();
|
||||
}
|
||||
|
||||
S32 LLGlobalEconomy::calculateTeleportCost(F32 distance) const
|
||||
S32 LLBaseEconomy::calculateTeleportCost(F32 distance) const
|
||||
{
|
||||
S32 min_cost = getTeleportMinPrice();
|
||||
F32 exponent = getTeleportPriceExponent();
|
||||
|
|
@ -135,13 +135,13 @@ S32 LLGlobalEconomy::calculateTeleportCost(F32 distance) const
|
|||
return cost;
|
||||
}
|
||||
|
||||
S32 LLGlobalEconomy::calculateLightRent(const LLVector3& object_size) const
|
||||
S32 LLBaseEconomy::calculateLightRent(const LLVector3& object_size) const
|
||||
{
|
||||
F32 intensity_mod = llmax(object_size.magVec(), 1.f);
|
||||
return (S32)(intensity_mod * getPriceRentLight());
|
||||
}
|
||||
|
||||
void LLGlobalEconomy::print()
|
||||
void LLBaseEconomy::print()
|
||||
{
|
||||
LL_INFOS() << "Global Economy Settings: " << LL_ENDL;
|
||||
LL_INFOS() << "Object Capacity: " << mObjectCapacity << LL_ENDL;
|
||||
|
|
@ -159,8 +159,7 @@ void LLGlobalEconomy::print()
|
|||
}
|
||||
|
||||
LLRegionEconomy::LLRegionEconomy()
|
||||
: LLGlobalEconomy(),
|
||||
mPriceObjectRent( -1.f ),
|
||||
: mPriceObjectRent( -1.f ),
|
||||
mPriceObjectScaleFactor( -1.f ),
|
||||
mEnergyEfficiency( -1.f ),
|
||||
mBasePriceParcelClaimDefault(-1),
|
||||
|
|
@ -187,7 +186,7 @@ void LLRegionEconomy::processEconomyData(LLMessageSystem *msg, void** user_data)
|
|||
|
||||
LLRegionEconomy *this_ptr = (LLRegionEconomy*)user_data;
|
||||
|
||||
LLGlobalEconomy::processEconomyData(msg, this_ptr);
|
||||
LLBaseEconomy::processEconomyData(msg, this_ptr);
|
||||
|
||||
msg->getS32Fast(_PREHASH_Info, _PREHASH_PriceParcelClaim, i);
|
||||
this_ptr->setBasePriceParcelClaimDefault(i);
|
||||
|
|
@ -252,7 +251,7 @@ S32 LLRegionEconomy::getPriceParcelRent() const
|
|||
|
||||
void LLRegionEconomy::print()
|
||||
{
|
||||
this->LLGlobalEconomy::print();
|
||||
this->LLBaseEconomy::print();
|
||||
|
||||
LL_INFOS() << "Region Economy Settings: " << LL_ENDL;
|
||||
LL_INFOS() << "Land (square meters): " << mAreaTotal << LL_ENDL;
|
||||
|
|
|
|||
|
|
@ -42,18 +42,11 @@ public:
|
|||
virtual void onEconomyDataChange() = 0;
|
||||
};
|
||||
|
||||
class LLGlobalEconomy
|
||||
class LLBaseEconomy
|
||||
{
|
||||
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() { }
|
||||
LLBaseEconomy();
|
||||
virtual ~LLBaseEconomy();
|
||||
|
||||
virtual void print();
|
||||
|
||||
|
|
@ -61,7 +54,7 @@ public:
|
|||
void removeObserver(LLEconomyObserver* observer);
|
||||
void notifyObservers();
|
||||
|
||||
static void processEconomyData(LLMessageSystem *msg, LLGlobalEconomy* econ_data);
|
||||
static void processEconomyData(LLMessageSystem *msg, LLBaseEconomy* econ_data);
|
||||
|
||||
S32 calculateTeleportCost(F32 distance) const;
|
||||
S32 calculateLightRent(const LLVector3& object_size) const;
|
||||
|
|
@ -108,8 +101,12 @@ private:
|
|||
std::list<LLEconomyObserver*> mObservers;
|
||||
};
|
||||
|
||||
class LLGlobalEconomy: public LLSingleton<LLGlobalEconomy>, public LLBaseEconomy
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLGlobalEconomy);
|
||||
};
|
||||
|
||||
class LLRegionEconomy : public LLGlobalEconomy
|
||||
class LLRegionEconomy : public LLBaseEconomy
|
||||
{
|
||||
public:
|
||||
LLRegionEconomy();
|
||||
|
|
|
|||
|
|
@ -51,8 +51,7 @@ struct FolderEntry : public LLDictionaryEntry
|
|||
class LLFolderDictionary : public LLSingleton<LLFolderDictionary>,
|
||||
public LLDictionary<LLFolderType::EType, FolderEntry>
|
||||
{
|
||||
public:
|
||||
LLFolderDictionary();
|
||||
LLSINGLETON(LLFolderDictionary);
|
||||
protected:
|
||||
virtual LLFolderType::EType notFound() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -64,8 +64,7 @@ struct InventoryEntry : public LLDictionaryEntry
|
|||
class LLInventoryDictionary : public LLSingleton<LLInventoryDictionary>,
|
||||
public LLDictionary<LLInventoryType::EType, InventoryEntry>
|
||||
{
|
||||
public:
|
||||
LLInventoryDictionary();
|
||||
LLSINGLETON(LLInventoryDictionary);
|
||||
};
|
||||
|
||||
LLInventoryDictionary::LLInventoryDictionary()
|
||||
|
|
|
|||
|
|
@ -254,6 +254,11 @@ inline int round_int(double x)
|
|||
}
|
||||
#endif // BOGUS_ROUND
|
||||
|
||||
inline F64 ll_round(const F64 val)
|
||||
{
|
||||
return F64(floor(val + 0.5f));
|
||||
}
|
||||
|
||||
inline F32 ll_round( F32 val, F32 nearest )
|
||||
{
|
||||
return F32(floor(val * (1.0f / nearest) + 0.5f)) * nearest;
|
||||
|
|
|
|||
|
|
@ -37,7 +37,8 @@ class LLCoprocedurePool;
|
|||
|
||||
class LLCoprocedureManager : public LLSingleton < LLCoprocedureManager >
|
||||
{
|
||||
friend class LLSingleton < LLCoprocedureManager > ;
|
||||
LLSINGLETON(LLCoprocedureManager);
|
||||
virtual ~LLCoprocedureManager();
|
||||
|
||||
public:
|
||||
typedef boost::function<U32(const std::string &)> SettingQuery_t;
|
||||
|
|
@ -45,9 +46,6 @@ public:
|
|||
|
||||
typedef boost::function<void(LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t &, const LLUUID &id)> CoProcedure_t;
|
||||
|
||||
LLCoprocedureManager();
|
||||
virtual ~LLCoprocedureManager();
|
||||
|
||||
/// Places the coprocedure on the queue for processing.
|
||||
///
|
||||
/// @param name Is used for debugging and should identify this coroutine.
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ class LLUUID;
|
|||
|
||||
class LLExperienceCache: public LLSingleton < LLExperienceCache >
|
||||
{
|
||||
friend class LLSingleton < LLExperienceCache > ;
|
||||
LLSINGLETON(LLExperienceCache);
|
||||
|
||||
public:
|
||||
typedef boost::function<std::string(const std::string &)> CapabilityQuery_t;
|
||||
|
|
@ -103,7 +103,6 @@ public:
|
|||
static const int PROPERTY_SUSPENDED; // 1 << 7
|
||||
|
||||
private:
|
||||
LLExperienceCache();
|
||||
virtual ~LLExperienceCache();
|
||||
|
||||
virtual void initSingleton();
|
||||
|
|
|
|||
|
|
@ -218,14 +218,14 @@ enum LLSocks5AuthType
|
|||
*/
|
||||
class LLProxy: public LLSingleton<LLProxy>
|
||||
{
|
||||
LOG_CLASS(LLProxy);
|
||||
public:
|
||||
/*###########################################################################################
|
||||
METHODS THAT DO NOT LOCK mProxyMutex!
|
||||
###########################################################################################*/
|
||||
// Constructor, cannot have parameters due to LLSingleton parent class. Call from main thread only.
|
||||
LLProxy();
|
||||
LLSINGLETON(LLProxy);
|
||||
LOG_CLASS(LLProxy);
|
||||
|
||||
public:
|
||||
// Static check for enabled status for UDP packets. Call from main thread only.
|
||||
static bool isSOCKSProxyEnabled() { return sUDPProxyEnabled; }
|
||||
|
||||
|
|
@ -239,9 +239,11 @@ public:
|
|||
/*###########################################################################################
|
||||
METHODS THAT LOCK mProxyMutex! DO NOT CALL WHILE mProxyMutex IS LOCKED!
|
||||
###########################################################################################*/
|
||||
private:
|
||||
// Destructor, closes open connections. Do not call directly, use cleanupClass().
|
||||
~LLProxy();
|
||||
|
||||
public:
|
||||
// Delete LLProxy singleton. Allows the apr_socket used in the SOCKS 5 control channel to be
|
||||
// destroyed before the call to apr_terminate. Call from main thread only.
|
||||
static void cleanupClass();
|
||||
|
|
|
|||
|
|
@ -98,12 +98,12 @@ void LLXfer_File::cleanup ()
|
|||
mFp = NULL;
|
||||
}
|
||||
|
||||
LLFile::remove(mTempFilename);
|
||||
LLFile::remove(mTempFilename, ENOENT);
|
||||
|
||||
if (mDeleteLocalOnCompletion)
|
||||
{
|
||||
LL_DEBUGS() << "Removing file: " << mLocalFilename << LL_ENDL;
|
||||
LLFile::remove(mLocalFilename);
|
||||
LLFile::remove(mLocalFilename, ENOENT);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -321,7 +321,7 @@ S32 LLXfer_File::processEOF()
|
|||
mCallbackResult = flushval;
|
||||
}
|
||||
|
||||
LLFile::remove(mLocalFilename);
|
||||
LLFile::remove(mLocalFilename, ENOENT);
|
||||
|
||||
if (!mCallbackResult)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -444,7 +444,7 @@ U64 LLXferManager::requestFile(const std::string& local_filename,
|
|||
&& (remote_filename.substr(remote_filename.length()-4) == ".tmp")
|
||||
&& gDirUtilp->fileExists(local_filename))
|
||||
{
|
||||
LLFile::remove(local_filename);
|
||||
LLFile::remove(local_filename, ENOENT);
|
||||
}
|
||||
xfer_id = getNextID();
|
||||
((LLXfer_File *)xferp)->initializeRequest(
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@
|
|||
#include "v4math.h"
|
||||
#include "lltransfertargetvfile.h"
|
||||
#include "llcorehttputil.h"
|
||||
#include "llpounceable.h"
|
||||
|
||||
#include "nd/ndexceptions.h" // <FS:ND/> For ndxran
|
||||
|
||||
|
|
@ -1735,7 +1736,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*/)
|
||||
|
|
@ -2652,7 +2655,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;
|
||||
|
|
@ -68,10 +69,10 @@ const S32 MESSAGE_MAX_PER_FRAME = 400;
|
|||
|
||||
class LLMessageStringTable : public LLSingleton<LLMessageStringTable>
|
||||
{
|
||||
public:
|
||||
LLMessageStringTable();
|
||||
LLSINGLETON(LLMessageStringTable);
|
||||
~LLMessageStringTable();
|
||||
|
||||
public:
|
||||
char *getString(const char *str);
|
||||
|
||||
U32 mUsed;
|
||||
|
|
@ -838,7 +839,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)
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@
|
|||
// init time. Use the lazy, on-demand initialization we get from LLSingleton.
|
||||
class NetworkIO: public LLSingleton<NetworkIO>
|
||||
{
|
||||
public:
|
||||
LLSINGLETON(NetworkIO);
|
||||
NetworkIO():
|
||||
mServicePump(NULL),
|
||||
mDone(false)
|
||||
|
|
@ -69,6 +69,7 @@ public:
|
|||
boost::bind(&NetworkIO::done, this, _1));
|
||||
}
|
||||
|
||||
public:
|
||||
bool pump(F32 timeout=10)
|
||||
{
|
||||
// Reset the done flag so we don't pop out prematurely
|
||||
|
|
|
|||
|
|
@ -267,7 +267,14 @@ bool LLTexUnit::bind(LLTexture* texture, bool for_rendering, bool forceBind)
|
|||
}
|
||||
else
|
||||
{
|
||||
LL_WARNS() << "NULL LLTexUnit::bind texture" << LL_ENDL;
|
||||
if (texture)
|
||||
{
|
||||
LL_DEBUGS() << "NULL LLTexUnit::bind GL image" << LL_ENDL;
|
||||
}
|
||||
else
|
||||
{
|
||||
LL_DEBUGS() << "NULL LLTexUnit::bind texture" << LL_ENDL;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -286,7 +293,7 @@ bool LLTexUnit::bind(LLImageGL* texture, bool for_rendering, bool forceBind)
|
|||
|
||||
if(!texture)
|
||||
{
|
||||
LL_WARNS() << "NULL LLTexUnit::bind texture" << LL_ENDL;
|
||||
LL_DEBUGS() << "NULL LLTexUnit::bind texture" << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -48,10 +48,10 @@
|
|||
|
||||
class LLClipboard : public LLSingleton<LLClipboard>
|
||||
{
|
||||
public:
|
||||
LLClipboard();
|
||||
LLSINGLETON(LLClipboard);
|
||||
~LLClipboard();
|
||||
|
||||
public:
|
||||
// Clears the clipboard
|
||||
void reset();
|
||||
// Returns the state of the clipboard so client can know if it has been modified (comparing with tracked state)
|
||||
|
|
|
|||
|
|
@ -189,6 +189,9 @@ public:
|
|||
class LLCommandManager
|
||||
: public LLSingleton<LLCommandManager>
|
||||
{
|
||||
LLSINGLETON(LLCommandManager);
|
||||
~LLCommandManager();
|
||||
|
||||
public:
|
||||
struct Params : public LLInitParam::Block<Params>
|
||||
{
|
||||
|
|
@ -200,9 +203,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
LLCommandManager();
|
||||
~LLCommandManager();
|
||||
|
||||
U32 commandCount() const;
|
||||
LLCommand * getCommand(U32 commandIndex);
|
||||
LLCommand * getCommand(const LLCommandId& commandId);
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@
|
|||
class LLScrollContainer;
|
||||
|
||||
struct ContainerViewRegistry : public LLChildRegistry<ContainerViewRegistry>
|
||||
{};
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(ContainerViewRegistry);
|
||||
};
|
||||
|
||||
class LLContainerView : public LLView
|
||||
{
|
||||
|
|
|
|||
|
|
@ -114,6 +114,81 @@ bool LLFlatListView::addItem(LLPanel * item, const LLSD& value /*= LLUUID::null*
|
|||
return true;
|
||||
}
|
||||
|
||||
bool LLFlatListView::addItemPairs(pairs_list_t panel_list, bool rearrange /*= true*/)
|
||||
{
|
||||
if (!mItemComparator)
|
||||
{
|
||||
LL_WARNS_ONCE() << "No comparator specified for inserting FlatListView items." << LL_ENDL;
|
||||
return false;
|
||||
}
|
||||
if (panel_list.size() == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// presort list so that it will be easier to sort elements into mItemPairs
|
||||
panel_list.sort(ComparatorAdaptor(*mItemComparator));
|
||||
|
||||
pairs_const_iterator_t new_pair_it = panel_list.begin();
|
||||
item_pair_t* new_pair = *new_pair_it;
|
||||
pairs_iterator_t pair_it = mItemPairs.begin();
|
||||
item_pair_t* item_pair = *pair_it;
|
||||
|
||||
// sort panel_list into mItemPars
|
||||
while (new_pair_it != panel_list.end() && pair_it != mItemPairs.end())
|
||||
{
|
||||
if (!new_pair->first || new_pair->first->getParent() == mItemsPanel)
|
||||
{
|
||||
// iterator already used or we are reusing existing panel
|
||||
new_pair_it++;
|
||||
new_pair = *new_pair_it;
|
||||
}
|
||||
else if (mItemComparator->compare(new_pair->first, item_pair->first))
|
||||
{
|
||||
LLPanel* panel = new_pair->first;
|
||||
|
||||
mItemPairs.insert(pair_it, new_pair);
|
||||
mItemsPanel->addChild(panel);
|
||||
|
||||
//_4 is for MASK
|
||||
panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, new_pair, _4));
|
||||
panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, new_pair, _4));
|
||||
// Children don't accept the focus
|
||||
panel->setTabStop(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
pair_it++;
|
||||
item_pair = *pair_it;
|
||||
}
|
||||
}
|
||||
|
||||
// Add what is left of panel_list into the end of mItemPairs.
|
||||
for (; new_pair_it != panel_list.end(); ++new_pair_it)
|
||||
{
|
||||
item_pair_t* item_pair = *new_pair_it;
|
||||
LLPanel *panel = item_pair->first;
|
||||
if (panel && panel->getParent() != mItemsPanel)
|
||||
{
|
||||
mItemPairs.push_back(item_pair);
|
||||
mItemsPanel->addChild(panel);
|
||||
|
||||
//_4 is for MASK
|
||||
panel->setMouseDownCallback(boost::bind(&LLFlatListView::onItemMouseClick, this, item_pair, _4));
|
||||
panel->setRightMouseDownCallback(boost::bind(&LLFlatListView::onItemRightMouseClick, this, item_pair, _4));
|
||||
// Children don't accept the focus
|
||||
panel->setTabStop(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (rearrange)
|
||||
{
|
||||
rearrangeItems();
|
||||
notifyParentItemsRectChanged();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool LLFlatListView::insertItemAfter(LLPanel* after_item, LLPanel* item_to_add, const LLSD& value /*= LLUUID::null*/)
|
||||
{
|
||||
|
|
@ -1301,6 +1376,28 @@ void LLFlatListViewEx::setFilterSubString(const std::string& filter_str)
|
|||
}
|
||||
}
|
||||
|
||||
void LLFlatListViewEx::updateItemVisibility(LLPanel* item, const LLSD &action)
|
||||
{
|
||||
if (!item) return;
|
||||
|
||||
// 0 signifies that filter is matched,
|
||||
// i.e. we don't hide items that don't support 'match_filter' action, separators etc.
|
||||
if (0 == item->notify(action))
|
||||
{
|
||||
mHasMatchedItems = true;
|
||||
item->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: implement (re)storing of current selection.
|
||||
if (!mForceShowingUnmatchedItems)
|
||||
{
|
||||
selectItem(item, false);
|
||||
}
|
||||
item->setVisible(mForceShowingUnmatchedItems);
|
||||
}
|
||||
}
|
||||
|
||||
void LLFlatListViewEx::filterItems()
|
||||
{
|
||||
typedef std::vector <LLPanel*> item_panel_list_t;
|
||||
|
|
@ -1321,22 +1418,7 @@ void LLFlatListViewEx::filterItems()
|
|||
iter != iter_end; ++iter)
|
||||
{
|
||||
LLPanel* pItem = (*iter);
|
||||
// 0 signifies that filter is matched,
|
||||
// i.e. we don't hide items that don't support 'match_filter' action, separators etc.
|
||||
if (0 == pItem->notify(action))
|
||||
{
|
||||
mHasMatchedItems = true;
|
||||
pItem->setVisible(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
// TODO: implement (re)storing of current selection.
|
||||
if(!mForceShowingUnmatchedItems)
|
||||
{
|
||||
selectItem(pItem, false);
|
||||
}
|
||||
pItem->setVisible(mForceShowingUnmatchedItems);
|
||||
}
|
||||
updateItemVisibility(pItem, action);
|
||||
}
|
||||
|
||||
sort();
|
||||
|
|
|
|||
|
|
@ -359,6 +359,8 @@ protected:
|
|||
|
||||
virtual bool removeItemPair(item_pair_t* item_pair, bool rearrange);
|
||||
|
||||
bool addItemPairs(pairs_list_t panel_list, bool rearrange = true);
|
||||
|
||||
/**
|
||||
* Notify parent about changed size of internal controls with "size_changes" action
|
||||
*
|
||||
|
|
@ -494,6 +496,7 @@ public:
|
|||
* Sets up new filter string and filters the list.
|
||||
*/
|
||||
void setFilterSubString(const std::string& filter_str);
|
||||
std::string getFilterSubString() { return mFilterSubString; }
|
||||
|
||||
/**
|
||||
* Filters the list, rearranges and notifies parent about shape changes.
|
||||
|
|
@ -517,6 +520,14 @@ protected:
|
|||
*/
|
||||
void updateNoItemsMessage(const std::string& filter_string);
|
||||
|
||||
/**
|
||||
* Applies visibility acording to action and LLFlatListView settings.
|
||||
*
|
||||
* @param item - item we are changing
|
||||
* @param item - action - parameters to determin visibility from
|
||||
*/
|
||||
void updateItemVisibility(LLPanel* item, const LLSD &action);
|
||||
|
||||
private:
|
||||
std::string mNoFilteredItemsMsg;
|
||||
std::string mNoItemsMsg;
|
||||
|
|
|
|||
|
|
@ -53,14 +53,8 @@
|
|||
template <typename FUNCTOR_TYPE>
|
||||
class LLFunctorRegistry : public LLSingleton<LLFunctorRegistry<FUNCTOR_TYPE> >
|
||||
{
|
||||
friend class LLSingleton<LLFunctorRegistry>;
|
||||
LLSINGLETON(LLFunctorRegistry);
|
||||
LOG_CLASS(LLFunctorRegistry);
|
||||
private:
|
||||
LLFunctorRegistry() : LOGFUNCTOR("LogFunctor"), DONOTHING("DoNothing")
|
||||
{
|
||||
mMap[LOGFUNCTOR] = log_functor;
|
||||
mMap[DONOTHING] = do_nothing;
|
||||
}
|
||||
|
||||
public:
|
||||
typedef FUNCTOR_TYPE ResponseFunctor;
|
||||
|
|
@ -124,6 +118,14 @@ private:
|
|||
FunctorMap mMap;
|
||||
};
|
||||
|
||||
template <typename FUNCTOR_TYPE>
|
||||
LLFunctorRegistry<FUNCTOR_TYPE>::LLFunctorRegistry() :
|
||||
LOGFUNCTOR("LogFunctor"), DONOTHING("DoNothing")
|
||||
{
|
||||
mMap[LOGFUNCTOR] = log_functor;
|
||||
mMap[DONOTHING] = do_nothing;
|
||||
}
|
||||
|
||||
template <typename FUNCTOR_TYPE>
|
||||
class LLFunctorRegistration
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ class LLLayoutStack : public LLView, public LLInstanceTracker<LLLayoutStack>
|
|||
public:
|
||||
|
||||
struct LayoutStackRegistry : public LLChildRegistry<LayoutStackRegistry>
|
||||
{};
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LayoutStackRegistry);
|
||||
};
|
||||
|
||||
struct Params : public LLInitParam::Block<Params, LLView::Params>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -351,7 +351,9 @@ private:
|
|||
|
||||
// child widget registry
|
||||
struct MenuRegistry : public LLChildRegistry<MenuRegistry>
|
||||
{};
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(MenuRegistry);
|
||||
};
|
||||
|
||||
|
||||
class LLMenuGL
|
||||
|
|
|
|||
|
|
@ -890,9 +890,9 @@ class LLNotifications :
|
|||
public LLSingleton<LLNotifications>,
|
||||
public LLNotificationChannelBase
|
||||
{
|
||||
LLSINGLETON(LLNotifications);
|
||||
LOG_CLASS(LLNotifications);
|
||||
|
||||
friend class LLSingleton<LLNotifications>;
|
||||
public:
|
||||
|
||||
// Needed to clear up RefCounted things prior to actual destruction
|
||||
|
|
@ -972,8 +972,6 @@ public:
|
|||
bool isVisibleByRules(LLNotificationPtr pNotification);
|
||||
|
||||
private:
|
||||
// we're a singleton, so we don't have a public constructor
|
||||
LLNotifications();
|
||||
/*virtual*/ void initSingleton();
|
||||
|
||||
void loadPersistentNotifications();
|
||||
|
|
|
|||
|
|
@ -268,8 +268,9 @@ typedef boost::function<LLPanel* (void)> LLPanelClassCreatorFunc;
|
|||
class LLRegisterPanelClass
|
||||
: public LLSingleton< LLRegisterPanelClass >
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLRegisterPanelClass);
|
||||
public:
|
||||
// reigister with either the provided builder, or the generic templated builder
|
||||
// register with either the provided builder, or the generic templated builder
|
||||
void addPanelClass(const std::string& tag,LLPanelClassCreatorFunc func)
|
||||
{
|
||||
mPanelClassesNames[tag] = func;
|
||||
|
|
|
|||
|
|
@ -42,9 +42,9 @@ enum LLLOCALE_ID
|
|||
|
||||
class LLResMgr : public LLSingleton<LLResMgr>
|
||||
{
|
||||
public:
|
||||
LLResMgr();
|
||||
LLSINGLETON(LLResMgr);
|
||||
|
||||
public:
|
||||
void setLocale( LLLOCALE_ID locale_id );
|
||||
LLLOCALE_ID getLocale() const { return mLocale; }
|
||||
|
||||
|
|
|
|||
|
|
@ -48,7 +48,9 @@ class LLUICtrlFactory;
|
|||
*****************************************************************************/
|
||||
|
||||
struct ScrollContainerRegistry : public LLChildRegistry<ScrollContainerRegistry>
|
||||
{};
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(ScrollContainerRegistry);
|
||||
};
|
||||
|
||||
class LLScrollContainer : public LLUICtrl
|
||||
{
|
||||
|
|
|
|||
|
|
@ -29,16 +29,15 @@
|
|||
|
||||
#include "llsingleton.h"
|
||||
#include "llui.h"
|
||||
#include "llinitdestroyclass.h"
|
||||
#include <boost/signals2.hpp>
|
||||
|
||||
class Hunspell;
|
||||
|
||||
class LLSpellChecker : public LLSingleton<LLSpellChecker>, public LLInitClass<LLSpellChecker>
|
||||
{
|
||||
friend class LLSingleton<LLSpellChecker>;
|
||||
LLSINGLETON(LLSpellChecker);
|
||||
friend class LLInitClass<LLSpellChecker>;
|
||||
protected:
|
||||
LLSpellChecker();
|
||||
~LLSpellChecker();
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -158,10 +158,7 @@ F32 clamp_precision(F32 value, S32 decimal_precision)
|
|||
for (S32 i = 0; i < decimal_precision; i++)
|
||||
clamped_value *= 10.0;
|
||||
|
||||
// <FS:Ansariel> FIRE-5630: Round with additional precision because of small numbers
|
||||
//clamped_value = ll_round((F32)clamped_value);
|
||||
clamped_value = floor(clamped_value + 0.5);
|
||||
// </FS:Ansariel>
|
||||
clamped_value = ll_round(clamped_value);
|
||||
|
||||
for (S32 i = 0; i < decimal_precision; i++)
|
||||
clamped_value /= 10.0;
|
||||
|
|
|
|||
|
|
@ -35,7 +35,9 @@ class LLStatBar;
|
|||
|
||||
// widget registrars
|
||||
struct StatViewRegistry : public LLChildRegistry<StatViewRegistry>
|
||||
{};
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(StatViewRegistry);
|
||||
};
|
||||
|
||||
class LLStatView : public LLContainerView
|
||||
{
|
||||
|
|
|
|||
|
|
@ -37,14 +37,14 @@ class LLColor4;
|
|||
|
||||
class LLTextParser : public LLSingleton<LLTextParser>
|
||||
{
|
||||
LLSINGLETON(LLTextParser);
|
||||
|
||||
public:
|
||||
typedef enum e_condition_type { CONTAINS, MATCHES, STARTS_WITH, ENDS_WITH } EConditionType;
|
||||
typedef enum e_highlight_type { PART, ALL } EHighlightType;
|
||||
typedef enum e_highlight_position { WHOLE, START, MIDDLE, END } EHighlightPosition;
|
||||
typedef enum e_dialog_action { ACTION_NONE, ACTION_CLOSE, ACTION_ADD, ACTION_COPY, ACTION_UPDATE } EDialogAction;
|
||||
|
||||
LLTextParser();
|
||||
|
||||
LLSD parsePartialLineHighlights(const std::string &text,const LLColor4 &color, EHighlightPosition part=WHOLE, S32 index=0);
|
||||
bool parseFullLineHighlights(const std::string &text, LLColor4 *color);
|
||||
|
||||
|
|
|
|||
|
|
@ -129,9 +129,10 @@ public:
|
|||
|
||||
class LLToolTipMgr : public LLSingleton<LLToolTipMgr>
|
||||
{
|
||||
LLSINGLETON(LLToolTipMgr);
|
||||
LOG_CLASS(LLToolTipMgr);
|
||||
|
||||
public:
|
||||
LLToolTipMgr();
|
||||
void show(const LLToolTip::Params& params);
|
||||
void show(const std::string& message);
|
||||
|
||||
|
|
|
|||
|
|
@ -60,6 +60,7 @@
|
|||
#include "llflyoutbutton.h"
|
||||
#include "llsearcheditor.h"
|
||||
#include "lltoolbar.h"
|
||||
#include "llcleanup.h"
|
||||
|
||||
// for XUIParse
|
||||
#include "llquaternion.h"
|
||||
|
|
@ -235,7 +236,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)
|
||||
|
|
|
|||
|
|
@ -352,95 +352,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>
|
||||
{
|
||||
|
|
|
|||
|
|
@ -38,7 +38,8 @@ class LLUIColor;
|
|||
|
||||
class LLUIColorTable : public LLSingleton<LLUIColorTable>
|
||||
{
|
||||
LOG_CLASS(LLUIColorTable);
|
||||
LLSINGLETON_EMPTY_CTOR(LLUIColorTable);
|
||||
LOG_CLASS(LLUIColorTable);
|
||||
|
||||
// consider using sorted vector, can be much faster
|
||||
|
||||
|
|
|
|||
|
|
@ -277,18 +277,25 @@ public:
|
|||
|
||||
class LLTextInputFilter : public LLQueryFilter, public LLSingleton<LLTextInputFilter>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLTextInputFilter);
|
||||
/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const
|
||||
{
|
||||
return filterResult_t(view->isCtrl() && static_cast<const LLUICtrl *>(view)->acceptsTextInput(), TRUE);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template <typename F, typename DERIVED> class CallbackRegistry : public LLRegistrySingleton<std::string, F, DERIVED >
|
||||
{};
|
||||
|
||||
class CommitCallbackRegistry : public CallbackRegistry<commit_callback_t, CommitCallbackRegistry>{};
|
||||
class CommitCallbackRegistry : public CallbackRegistry<commit_callback_t, CommitCallbackRegistry>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(CommitCallbackRegistry);
|
||||
};
|
||||
// the enable callback registry is also used for visiblity callbacks
|
||||
class EnableCallbackRegistry : public CallbackRegistry<enable_callback_t, EnableCallbackRegistry>{};
|
||||
class EnableCallbackRegistry : public CallbackRegistry<enable_callback_t, EnableCallbackRegistry>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(EnableCallbackRegistry);
|
||||
};
|
||||
|
||||
protected:
|
||||
|
||||
|
|
|
|||
|
|
@ -33,6 +33,8 @@
|
|||
#include "llxuiparser.h"
|
||||
#include "llstl.h"
|
||||
#include "lldir.h"
|
||||
#include "llsingleton.h"
|
||||
#include "llheteromap.h"
|
||||
|
||||
class LLView;
|
||||
|
||||
|
|
@ -57,22 +59,24 @@ protected:
|
|||
|
||||
class LLDefaultChildRegistry : public LLChildRegistry<LLDefaultChildRegistry>
|
||||
{
|
||||
protected:
|
||||
LLDefaultChildRegistry(){}
|
||||
friend class LLSingleton<LLDefaultChildRegistry>;
|
||||
LLSINGLETON_EMPTY_CTOR(LLDefaultChildRegistry);
|
||||
};
|
||||
|
||||
// lookup widget name by type
|
||||
class LLWidgetNameRegistry
|
||||
: public LLRegistrySingleton<const std::type_info*, std::string, LLWidgetNameRegistry>
|
||||
{};
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLWidgetNameRegistry);
|
||||
};
|
||||
|
||||
// lookup function for generating empty param block by widget type
|
||||
// this is used for schema generation
|
||||
//typedef const LLInitParam::BaseBlock& (*empty_param_block_func_t)();
|
||||
//class LLDefaultParamBlockRegistry
|
||||
//: public LLRegistrySingleton<const std::type_info*, empty_param_block_func_t, LLDefaultParamBlockRegistry>
|
||||
//{};
|
||||
//{
|
||||
// LLSINGLETON(LLDefaultParamBlockRegistry);
|
||||
//};
|
||||
|
||||
extern LLTrace::BlockTimerStatHandle FTM_WIDGET_SETUP;
|
||||
extern LLTrace::BlockTimerStatHandle FTM_WIDGET_CONSTRUCTION;
|
||||
|
|
@ -85,31 +89,15 @@ extern template class LLUICtrlFactory* LLSingleton<class LLUICtrlFactory>::getIn
|
|||
|
||||
class LLUICtrlFactory : public LLSingleton<LLUICtrlFactory>
|
||||
{
|
||||
private:
|
||||
friend class LLSingleton<LLUICtrlFactory>;
|
||||
LLUICtrlFactory();
|
||||
LLSINGLETON(LLUICtrlFactory);
|
||||
~LLUICtrlFactory();
|
||||
|
||||
// only partial specialization allowed in inner classes, so use extra dummy parameter
|
||||
template <typename PARAM_BLOCK, int DUMMY>
|
||||
class ParamDefaults : public LLSingleton<ParamDefaults<PARAM_BLOCK, DUMMY> >
|
||||
class ParamDefaults
|
||||
{
|
||||
public:
|
||||
ParamDefaults()
|
||||
{
|
||||
// look up template file for this param block...
|
||||
const std::string* param_block_tag = LLWidgetNameRegistry::instance().getValue(&typeid(PARAM_BLOCK));
|
||||
if (param_block_tag)
|
||||
{ // ...and if it exists, back fill values using the most specific template first
|
||||
PARAM_BLOCK params;
|
||||
LLUICtrlFactory::loadWidgetTemplate(*param_block_tag, params);
|
||||
mPrototype.fillFrom(params);
|
||||
}
|
||||
// recursively fill from base class param block
|
||||
((typename PARAM_BLOCK::base_block_t&)mPrototype).fillFrom(ParamDefaults<typename PARAM_BLOCK::base_block_t, DUMMY>::instance().get());
|
||||
|
||||
}
|
||||
|
||||
ParamDefaults();
|
||||
const PARAM_BLOCK& get() { return mPrototype; }
|
||||
|
||||
private:
|
||||
|
|
@ -118,9 +106,10 @@ private:
|
|||
|
||||
// base case for recursion, there are NO base classes of LLInitParam::BaseBlock
|
||||
template<int DUMMY>
|
||||
class ParamDefaults<LLInitParam::BaseBlock, DUMMY> : public LLSingleton<ParamDefaults<LLInitParam::BaseBlock, DUMMY> >
|
||||
class ParamDefaults<LLInitParam::BaseBlock, DUMMY>
|
||||
{
|
||||
public:
|
||||
ParamDefaults();
|
||||
const LLInitParam::BaseBlock& get() { return mBaseBlock; }
|
||||
private:
|
||||
LLInitParam::BaseBlock mBaseBlock;
|
||||
|
|
@ -132,7 +121,7 @@ public:
|
|||
template<typename T>
|
||||
static const typename T::Params& getDefaultParams()
|
||||
{
|
||||
return ParamDefaults<typename T::Params, 0>::instance().get();
|
||||
return instance().mParamDefaultsMap.obtain< ParamDefaults<typename T::Params, 0> >().get();
|
||||
}
|
||||
|
||||
// Does what you want for LLFloaters and LLPanels
|
||||
|
|
@ -147,7 +136,8 @@ public:
|
|||
template<typename T>
|
||||
static T* create(typename T::Params& params, LLView* parent = NULL)
|
||||
{
|
||||
params.fillFrom(ParamDefaults<typename T::Params, 0>::instance().get());
|
||||
params.fillFrom(instance().mParamDefaultsMap.obtain<
|
||||
ParamDefaults<typename T::Params, 0> >().get());
|
||||
|
||||
T* widget = createWidgetImpl<T>(params, parent);
|
||||
if (widget)
|
||||
|
|
@ -295,8 +285,40 @@ private:
|
|||
|
||||
class LLPanel* mDummyPanel;
|
||||
std::vector<std::string> mFileNames;
|
||||
|
||||
// store ParamDefaults specializations
|
||||
// Each ParamDefaults specialization used to be an LLSingleton in its own
|
||||
// right. But the 2016 changes to the LLSingleton mechanism, making
|
||||
// LLSingleton instances polymorphic, are incompatible with current
|
||||
// LLInitParam::BaseBlock functionality. (Thanks NickyD for spotting
|
||||
// that!) Moreover, instances of the private nested ParamDefaults template
|
||||
// aren't global resources -- which is what LLSingleton is designed for.
|
||||
// This is simply a cache looked up by type. Its lifespan is tied to
|
||||
// LLUICtrlFactory. Use LLHeteroMap for this cache.
|
||||
LLHeteroMap mParamDefaultsMap;
|
||||
};
|
||||
|
||||
template <typename PARAM_BLOCK, int DUMMY>
|
||||
LLUICtrlFactory::ParamDefaults<PARAM_BLOCK, DUMMY>::ParamDefaults()
|
||||
{
|
||||
// look up template file for this param block...
|
||||
const std::string* param_block_tag = LLWidgetNameRegistry::instance().getValue(&typeid(PARAM_BLOCK));
|
||||
if (param_block_tag)
|
||||
{ // ...and if it exists, back fill values using the most specific template first
|
||||
PARAM_BLOCK params;
|
||||
LLUICtrlFactory::loadWidgetTemplate(*param_block_tag, params);
|
||||
mPrototype.fillFrom(params);
|
||||
}
|
||||
// recursively fill from base class param block
|
||||
((typename PARAM_BLOCK::base_block_t&)mPrototype).fillFrom(
|
||||
LLUICtrlFactory::instance().mParamDefaultsMap.obtain<
|
||||
ParamDefaults<typename PARAM_BLOCK::base_block_t, DUMMY> >().get());
|
||||
|
||||
}
|
||||
|
||||
template <int DUMMY>
|
||||
LLUICtrlFactory::ParamDefaults<LLInitParam::BaseBlock, DUMMY>::ParamDefaults() {}
|
||||
|
||||
// this is here to make gcc happy with reference to LLUICtrlFactory
|
||||
template<typename DERIVED>
|
||||
template<typename T>
|
||||
|
|
|
|||
|
|
@ -62,9 +62,9 @@ void LLUrlRegistryNullCallback(const std::string &url,
|
|||
///
|
||||
class LLUrlRegistry : public LLSingleton<LLUrlRegistry>
|
||||
{
|
||||
public:
|
||||
LLSINGLETON(LLUrlRegistry);
|
||||
~LLUrlRegistry();
|
||||
|
||||
public:
|
||||
/// add a new Url handler to the registry (will be freed on destruction)
|
||||
/// optionally force it to the front of the list, making it take
|
||||
/// priority over other regular expression matches for URLs
|
||||
|
|
@ -89,9 +89,6 @@ public:
|
|||
bool isUrl(const LLWString &text);
|
||||
|
||||
private:
|
||||
LLUrlRegistry();
|
||||
friend class LLSingleton<LLUrlRegistry>;
|
||||
|
||||
std::vector<LLUrlEntryBase *> mUrlEntry;
|
||||
LLUrlEntryBase* mUrlEntryTrusted;
|
||||
LLUrlEntryBase* mUrlEntryIcon;
|
||||
|
|
|
|||
|
|
@ -1973,6 +1973,7 @@ private:
|
|||
|
||||
class SortByTabOrder : public LLQuerySorter, public LLSingleton<SortByTabOrder>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(SortByTabOrder);
|
||||
/*virtual*/ void sort(LLView * parent, LLView::child_list_t &children) const
|
||||
{
|
||||
children.sort(CompareByTabOrder(parent->getTabOrder(), parent->getDefaultTabGroup()));
|
||||
|
|
@ -1996,6 +1997,7 @@ const LLViewQuery & LLView::getTabOrderQuery()
|
|||
// This class is only used internally by getFocusRootsQuery below.
|
||||
class LLFocusRootsFilter : public LLQueryFilter, public LLSingleton<LLFocusRootsFilter>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLFocusRootsFilter);
|
||||
/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const
|
||||
{
|
||||
return filterResult_t(view->isCtrl() && view->isFocusRoot(), !view->isFocusRoot());
|
||||
|
|
|
|||
|
|
@ -44,13 +44,10 @@
|
|||
|
||||
class LLViewerEventRecorder : public LLSingleton<LLViewerEventRecorder>
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
LLViewerEventRecorder(); // TODO Protect constructor better if we can (not happy in private section) - could add a factory... - we are singleton
|
||||
LLSINGLETON(LLViewerEventRecorder);
|
||||
~LLViewerEventRecorder();
|
||||
|
||||
|
||||
public:
|
||||
void updateMouseEventInfo(S32 local_x,S32 local_y, S32 global_x, S32 global_y, std::string mName);
|
||||
void setMouseLocalCoords(S32 x,S32 y);
|
||||
void setMouseGlobalCoords(S32 x,S32 y);
|
||||
|
|
|
|||
|
|
@ -54,31 +54,37 @@ public:
|
|||
|
||||
class LLLeavesFilter : public LLQueryFilter, public LLSingleton<LLLeavesFilter>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLLeavesFilter);
|
||||
/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
|
||||
};
|
||||
|
||||
class LLRootsFilter : public LLQueryFilter, public LLSingleton<LLRootsFilter>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLRootsFilter);
|
||||
/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
|
||||
};
|
||||
|
||||
class LLVisibleFilter : public LLQueryFilter, public LLSingleton<LLVisibleFilter>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLVisibleFilter);
|
||||
/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
|
||||
};
|
||||
|
||||
class LLEnabledFilter : public LLQueryFilter, public LLSingleton<LLEnabledFilter>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLEnabledFilter);
|
||||
/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
|
||||
};
|
||||
|
||||
class LLTabStopFilter : public LLQueryFilter, public LLSingleton<LLTabStopFilter>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLTabStopFilter);
|
||||
/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
|
||||
};
|
||||
|
||||
class LLCtrlFilter : public LLQueryFilter, public LLSingleton<LLCtrlFilter>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLCtrlFilter);
|
||||
/*virtual*/ filterResult_t operator() (const LLView* const view, const viewList_t & children) const;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,9 @@ class LLView;
|
|||
// lookup widget type by name
|
||||
class LLWidgetTypeRegistry
|
||||
: public LLRegistrySingleton<std::string, const std::type_info*, LLWidgetTypeRegistry>
|
||||
{};
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLWidgetTypeRegistry);
|
||||
};
|
||||
|
||||
|
||||
// global static instance for registering all widget types
|
||||
|
|
@ -51,7 +53,9 @@ typedef LLRegistry<std::string, LLWidgetCreatorFunc> widget_registry_t;
|
|||
|
||||
class LLChildRegistryRegistry
|
||||
: public LLRegistrySingleton<const std::type_info*, widget_registry_t, LLChildRegistryRegistry>
|
||||
{};
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(LLChildRegistryRegistry);
|
||||
};
|
||||
|
||||
class LLXSDWriter : public LLInitParam::Parser
|
||||
{
|
||||
|
|
@ -60,7 +64,7 @@ public:
|
|||
void writeXSD(const std::string& name, LLXMLNodePtr node, const LLInitParam::BaseBlock& block, const std::string& xml_namespace);
|
||||
|
||||
/*virtual*/ std::string getCurrentElementName() { return LLStringUtil::null; }
|
||||
|
||||
/*virtual*/ std::string getCurrentFileName() { return LLStringUtil::null; }
|
||||
LLXSDWriter();
|
||||
~LLXSDWriter();
|
||||
|
||||
|
|
@ -98,6 +102,7 @@ public:
|
|||
typedef LLInitParam::Parser::name_stack_t name_stack_t;
|
||||
|
||||
/*virtual*/ std::string getCurrentElementName();
|
||||
/*virtual*/ std::string getCurrentFileName() { return mCurFileName; }
|
||||
/*virtual*/ void parserWarning(const std::string& message);
|
||||
/*virtual*/ void parserError(const std::string& message);
|
||||
|
||||
|
|
@ -200,6 +205,7 @@ public:
|
|||
virtual ~LLSimpleXUIParser();
|
||||
|
||||
/*virtual*/ std::string getCurrentElementName();
|
||||
/*virtual*/ std::string getCurrentFileName() { return mCurFileName; }
|
||||
/*virtual*/ void parserWarning(const std::string& message);
|
||||
/*virtual*/ void parserError(const std::string& message);
|
||||
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
#include "llcachename.h"
|
||||
#include "lluuid.h"
|
||||
#include "message.h"
|
||||
#include "llpounceable.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
|
|
@ -174,7 +175,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
|
||||
|
|
|
|||
|
|
@ -31,17 +31,16 @@
|
|||
#include "llsingleton.h"
|
||||
class VolumeCatcherImpl : public LLSingleton<VolumeCatcherImpl>
|
||||
{
|
||||
friend LLSingleton<VolumeCatcherImpl>;
|
||||
LLSINGLETON(VolumeCatcherImpl);
|
||||
// This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
|
||||
~VolumeCatcherImpl();
|
||||
|
||||
public:
|
||||
|
||||
void setVolume(F32 volume);
|
||||
void setPan(F32 pan);
|
||||
|
||||
private:
|
||||
// This is a singleton class -- both callers and the component implementation should use getInstance() to find the instance.
|
||||
VolumeCatcherImpl();
|
||||
~VolumeCatcherImpl();
|
||||
|
||||
typedef void (WINAPI *set_volume_func_t)(F32);
|
||||
typedef void (WINAPI *set_mute_func_t)(bool);
|
||||
|
||||
|
|
|
|||
|
|
@ -91,7 +91,8 @@ private:
|
|||
|
||||
class NACLAntiSpamRegistry : public LLSingleton<NACLAntiSpamRegistry>
|
||||
{
|
||||
friend class LLSingleton<NACLAntiSpamRegistry>;
|
||||
LLSINGLETON(NACLAntiSpamRegistry);
|
||||
~NACLAntiSpamRegistry();
|
||||
|
||||
public:
|
||||
void setGlobalQueue(bool value);
|
||||
|
|
@ -117,9 +118,6 @@ public:
|
|||
void processObjectPropertiesFamily(LLMessageSystem* msg);
|
||||
|
||||
private:
|
||||
NACLAntiSpamRegistry();
|
||||
~NACLAntiSpamRegistry();
|
||||
|
||||
const char* getQueueName(EAntispamQueue queue);
|
||||
|
||||
void blockGlobalEntry(const LLUUID& source);
|
||||
|
|
|
|||
|
|
@ -43,24 +43,21 @@ class AnimationExplorer;
|
|||
class RecentAnimationList
|
||||
: public LLSingleton<RecentAnimationList>
|
||||
{
|
||||
friend class LLSingleton<RecentAnimationList>;
|
||||
LLSINGLETON(RecentAnimationList);
|
||||
~RecentAnimationList();
|
||||
|
||||
private:
|
||||
RecentAnimationList();
|
||||
~RecentAnimationList();
|
||||
public:
|
||||
struct AnimationEntry
|
||||
{
|
||||
LLUUID animationID; // asset ID of the animation
|
||||
LLUUID playedBy; // object/agent who played this animation
|
||||
F64 time; // time in seconds since viewer start when the animation started
|
||||
};
|
||||
|
||||
public:
|
||||
struct AnimationEntry
|
||||
{
|
||||
LLUUID animationID; // asset ID of the animation
|
||||
LLUUID playedBy; // object/agent who played this animation
|
||||
F64 time; // time in seconds since viewer start when the animation started
|
||||
};
|
||||
std::deque<AnimationEntry> mAnimationList;
|
||||
|
||||
std::deque<AnimationEntry> mAnimationList;
|
||||
|
||||
void addAnimation(const LLUUID& id, const LLUUID& playedBy); // called in llviewermessage.cpp
|
||||
void requestList(AnimationExplorer* explorer); // request animation list
|
||||
void addAnimation(const LLUUID& id, const LLUUID& playedBy); // called in llviewermessage.cpp
|
||||
void requestList(AnimationExplorer* explorer); // request animation list
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------------
|
||||
|
|
|
|||
|
|
@ -87,11 +87,8 @@ class LLVFS;
|
|||
class AOEngine
|
||||
: public LLSingleton<AOEngine>
|
||||
{
|
||||
friend class LLSingleton<AOEngine>;
|
||||
|
||||
private:
|
||||
AOEngine();
|
||||
~AOEngine();
|
||||
LLSINGLETON(AOEngine);
|
||||
~AOEngine();
|
||||
|
||||
public:
|
||||
enum eCycleMode
|
||||
|
|
|
|||
|
|
@ -285,6 +285,16 @@
|
|||
is_running_function="Floater.IsOpen"
|
||||
is_running_parameters="camera"
|
||||
/>
|
||||
<command name="reporter"
|
||||
available_in_toybox="true"
|
||||
icon="Command_Report_Abuse_Icon"
|
||||
label_ref="Command_Report_Abuse_Label"
|
||||
tooltip_ref="Command_Report_Abuse_Tooltip"
|
||||
execute_function="Floater.ToggleOrBringToFront"
|
||||
execute_parameters="reporter"
|
||||
is_running_function="Floater.IsOpen"
|
||||
is_running_parameters="reporter"
|
||||
/>
|
||||
|
||||
<!-- Firestorm command buttons -->
|
||||
<command name="voice"
|
||||
|
|
|
|||
|
|
@ -8991,6 +8991,17 @@
|
|||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>MuteListLimit</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Maximum number of entries in the mute list</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>S32</string>
|
||||
<key>Value</key>
|
||||
<integer>1000</integer>
|
||||
</map>
|
||||
<key>NearMeRange</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
|
|||
|
|
@ -486,6 +486,17 @@
|
|||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<key>PreviousScreenshotForReport</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Use Previous Screenshot for Abuse report</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
<string>Boolean</string>
|
||||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<!-- Settings below are for back compatibility only.
|
||||
They are not used in current viewer anymore. But they can't be removed to avoid
|
||||
influence on previous versions of the viewer in case of settings are not used or default value
|
||||
|
|
@ -511,7 +522,7 @@
|
|||
<key>LogChat</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Log Chat</string>
|
||||
<string>Obsolete - this setting is no longer used and has no effect.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
|
|
@ -522,7 +533,7 @@
|
|||
<key>LogChatIM</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Log Incoming Instant Messages with Chat</string>
|
||||
<string>Obsolete - this setting is no longer used and has no effect.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
|
|
@ -533,7 +544,7 @@
|
|||
<key>LogChatTimestamp</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
<string>Log Timestamp of Chat</string>
|
||||
<string>Obsolete - this setting is no longer used and has no effect.</string>
|
||||
<key>Persist</key>
|
||||
<integer>1</integer>
|
||||
<key>Type</key>
|
||||
|
|
@ -541,6 +552,7 @@
|
|||
<key>Value</key>
|
||||
<integer>0</integer>
|
||||
</map>
|
||||
<!-- End of back compatibility settings -->
|
||||
<key>TranslatingEnabled</key>
|
||||
<map>
|
||||
<key>Comment</key>
|
||||
|
|
@ -596,7 +608,6 @@
|
|||
<key>Value</key>
|
||||
<integer>1</integer>
|
||||
</map>
|
||||
<!-- End of back compatibility settings -->
|
||||
|
||||
<!-- Firestorm settings -->
|
||||
<key>DebugLookAt</key>
|
||||
|
|
|
|||
|
|
@ -28,15 +28,6 @@
|
|||
|
||||
#include "llviewercontrol.h"
|
||||
|
||||
DialogStack::DialogStack() :
|
||||
LLSingleton<DialogStack>()
|
||||
{
|
||||
}
|
||||
|
||||
DialogStack::~DialogStack()
|
||||
{
|
||||
}
|
||||
|
||||
void DialogStack::update()
|
||||
{
|
||||
// show dialog stack browse icon when more than one dialog is on the screen
|
||||
|
|
|
|||
|
|
@ -30,11 +30,8 @@
|
|||
class DialogStack
|
||||
: public LLSingleton<DialogStack>
|
||||
{
|
||||
friend class LLSingleton<DialogStack>;
|
||||
|
||||
private:
|
||||
DialogStack();
|
||||
~DialogStack();
|
||||
LLSINGLETON_EMPTY_CTOR(DialogStack);
|
||||
~DialogStack() {}
|
||||
|
||||
protected:
|
||||
void update();
|
||||
|
|
|
|||
|
|
@ -24,8 +24,9 @@
|
|||
|
||||
class exoGroupMuteList : public LLSingleton<exoGroupMuteList>
|
||||
{
|
||||
LLSINGLETON(exoGroupMuteList);
|
||||
|
||||
public:
|
||||
exoGroupMuteList();
|
||||
bool isMuted(const LLUUID &group) const;
|
||||
void add(const LLUUID &group);
|
||||
void remove(const LLUUID &group);
|
||||
|
|
|
|||
|
|
@ -36,12 +36,13 @@ public:
|
|||
|
||||
class exoPostProcess : public LLSingleton<exoPostProcess>
|
||||
{
|
||||
friend class LLSingleton<exoPostProcess>;
|
||||
LLSINGLETON(exoPostProcess);
|
||||
~exoPostProcess();
|
||||
|
||||
private:
|
||||
static exoPostProcess *postProcess;
|
||||
exoPostProcess();
|
||||
|
||||
public:
|
||||
~exoPostProcess();
|
||||
enum ExodusRenderPostType
|
||||
{
|
||||
//EXODUS_RENDER_GAMMA_POST = 1,
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@
|
|||
#define FS_AREASEARCH_H
|
||||
|
||||
#include "llfloater.h"
|
||||
#include "llsingleton.h"
|
||||
#include "llframetimer.h"
|
||||
#include "llsaleinfo.h"
|
||||
#include "llcategory.h"
|
||||
|
|
@ -102,7 +101,7 @@ struct FSObjectProperties
|
|||
// Main class for area search
|
||||
// holds the search engine and main floater
|
||||
// --------------------------------------------------------------------
|
||||
class FSAreaSearch : public LLSingleton<FSAreaSearch>, public LLFloater
|
||||
class FSAreaSearch : public LLFloater
|
||||
{
|
||||
LOG_CLASS(FSAreaSearch);
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -41,6 +41,8 @@ typedef boost::unordered_map<LLUUID, LLSD, FSUUIDHash> blacklist_data_t;
|
|||
|
||||
class FSAssetBlacklist : public LLSingleton<FSAssetBlacklist>
|
||||
{
|
||||
LLSINGLETON_EMPTY_CTOR(FSAssetBlacklist);
|
||||
|
||||
public:
|
||||
void init();
|
||||
bool isBlacklisted(const LLUUID& id, LLAssetType::EType type);
|
||||
|
|
|
|||
|
|
@ -35,9 +35,11 @@ class FSAvatarRenderPersistence
|
|||
{
|
||||
LOG_CLASS(FSAvatarRenderPersistence);
|
||||
|
||||
friend class LLSingleton<FSAvatarRenderPersistence>;
|
||||
friend class FSPanelPreferenceBackup;
|
||||
|
||||
LLSINGLETON(FSAvatarRenderPersistence);
|
||||
virtual ~FSAvatarRenderPersistence();
|
||||
|
||||
public:
|
||||
void init();
|
||||
|
||||
|
|
@ -54,9 +56,6 @@ public:
|
|||
}
|
||||
|
||||
private:
|
||||
FSAvatarRenderPersistence();
|
||||
virtual ~FSAvatarRenderPersistence();
|
||||
|
||||
void loadAvatarRenderSettings();
|
||||
void saveAvatarRenderSettings();
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue