Merge viewer-lynx

master
Ansariel 2016-11-16 09:55:54 +01:00
commit c182741596
307 changed files with 3830 additions and 1444 deletions

View File

@ -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

View File

@ -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();

View File

@ -127,8 +127,7 @@ class LLAvatarAppearanceDictionary : public LLSingleton<LLAvatarAppearanceDictio
//--------------------------------------------------------------------
// Constructors and Destructors
//--------------------------------------------------------------------
public:
LLAvatarAppearanceDictionary();
LLSINGLETON(LLAvatarAppearanceDictionary);
virtual ~LLAvatarAppearanceDictionary();
private:
void createAssociations();

View File

@ -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();

View File

@ -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:

View File

@ -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

View File

@ -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

View File

@ -63,8 +63,7 @@ struct AssetEntry : public LLDictionaryEntry
class LLAssetDictionary : public LLSingleton<LLAssetDictionary>,
public LLDictionary<LLAssetType::EType, AssetEntry>
{
public:
LLAssetDictionary();
LLSINGLETON(LLAssetDictionary);
};
LLAssetDictionary::LLAssetDictionary()

View File

@ -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;
}

View File

@ -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) */

View File

@ -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);
}

View File

@ -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

View File

@ -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) */

View File

@ -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;

View File

@ -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();

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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)

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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) */

View File

@ -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();
}
}

View File

@ -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) */

View File

@ -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;
}

View File

@ -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; }

View File

@ -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)
{

View File

@ -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.

View File

@ -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) */

View File

@ -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();
}

View File

@ -66,6 +66,7 @@ public:
}
/*virtual*/ std::string getCurrentElementName();
/*virtual*/ std::string getCurrentFileName(){ return LLStringUtil::null; }
private:
void writeSDImpl(LLSD& sd,

View File

@ -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);
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)
}

View File

@ -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

3
indra/llcorehttp/tests/llcorehttp_test.cpp Normal file → Executable file
View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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;

View File

@ -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();

View File

@ -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
{

View File

@ -64,8 +64,7 @@ struct InventoryEntry : public LLDictionaryEntry
class LLInventoryDictionary : public LLSingleton<LLInventoryDictionary>,
public LLDictionary<LLInventoryType::EType, InventoryEntry>
{
public:
LLInventoryDictionary();
LLSINGLETON(LLInventoryDictionary);
};
LLInventoryDictionary::LLInventoryDictionary()

View File

@ -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;

View File

@ -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.

View File

@ -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();

View File

@ -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();

View File

@ -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)
{

View File

@ -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(

View File

@ -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;
}
}

View File

@ -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.

View File

@ -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);
}

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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;
}

View File

@ -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)

View File

@ -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);

View File

@ -35,7 +35,9 @@
class LLScrollContainer;
struct ContainerViewRegistry : public LLChildRegistry<ContainerViewRegistry>
{};
{
LLSINGLETON_EMPTY_CTOR(ContainerViewRegistry);
};
class LLContainerView : public LLView
{

View File

@ -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();

View File

@ -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;

View File

@ -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
{

View File

@ -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>
{

View File

@ -351,7 +351,9 @@ private:
// child widget registry
struct MenuRegistry : public LLChildRegistry<MenuRegistry>
{};
{
LLSINGLETON_EMPTY_CTOR(MenuRegistry);
};
class LLMenuGL

View File

@ -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();

View File

@ -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;

View File

@ -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; }

View File

@ -48,7 +48,9 @@ class LLUICtrlFactory;
*****************************************************************************/
struct ScrollContainerRegistry : public LLChildRegistry<ScrollContainerRegistry>
{};
{
LLSINGLETON_EMPTY_CTOR(ScrollContainerRegistry);
};
class LLScrollContainer : public LLUICtrl
{

View File

@ -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:

View File

@ -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;

View File

@ -35,7 +35,9 @@ class LLStatBar;
// widget registrars
struct StatViewRegistry : public LLChildRegistry<StatViewRegistry>
{};
{
LLSINGLETON_EMPTY_CTOR(StatViewRegistry);
};
class LLStatView : public LLContainerView
{

View File

@ -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);

View File

@ -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);

View File

@ -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)

View File

@ -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>
{

View File

@ -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

View File

@ -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:

View File

@ -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>

View File

@ -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;

View File

@ -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());

View File

@ -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);

View File

@ -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;
};

View File

@ -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);

3
indra/llui/tests/llurlentry_stub.cpp Normal file → Executable file
View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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
};
// --------------------------------------------------------------------------

View File

@ -87,11 +87,8 @@ class LLVFS;
class AOEngine
: public LLSingleton<AOEngine>
{
friend class LLSingleton<AOEngine>;
private:
AOEngine();
~AOEngine();
LLSINGLETON(AOEngine);
~AOEngine();
public:
enum eCycleMode

View File

@ -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"

View File

@ -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>

View File

@ -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>

View File

@ -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

View File

@ -30,11 +30,8 @@
class DialogStack
: public LLSingleton<DialogStack>
{
friend class LLSingleton<DialogStack>;
private:
DialogStack();
~DialogStack();
LLSINGLETON_EMPTY_CTOR(DialogStack);
~DialogStack() {}
protected:
void update();

View File

@ -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);

View File

@ -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,

View File

@ -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:

View File

@ -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);

View File

@ -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