diff options
83 files changed, 1971 insertions, 620 deletions
| @@ -501,3 +501,4 @@ d3d0101e980ec95043e0af9b7903045d3bc447e4 3.7.24-release  000e9dda4162cbf0a83ba88558b19473654a09a9 3.7.26-release  afd8d4756e8eda3c8f760625d1c17a2ad40ad6c8 3.7.27-release  566874eb5ab26c003ef7fb0e22ce40c5fa0013f4 3.7.28-release +d07f76c5b9860fb87924d00ca729f7d4532534d6 3.7.29-release diff --git a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp index 3d27b4a5b5..f4dba16a94 100755 --- a/indra/integration_tests/llimage_libtest/llimage_libtest.cpp +++ b/indra/integration_tests/llimage_libtest/llimage_libtest.cpp @@ -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(); diff --git a/indra/llappearance/llwearabledata.cpp b/indra/llappearance/llwearabledata.cpp index 5dfb201fc4..2bf3b9085b 100755 --- a/indra/llappearance/llwearabledata.cpp +++ b/indra/llappearance/llwearabledata.cpp @@ -92,7 +92,7 @@ void LLWearableData::setWearable(const LLWearableType::EType type, U32 index, LL  	}  } -U32 LLWearableData::pushWearable(const LLWearableType::EType type,  +void LLWearableData::pushWearable(const LLWearableType::EType type,   								   LLWearable *wearable,  								   bool trigger_updated /* = true */)  { @@ -100,9 +100,8 @@ U32 LLWearableData::pushWearable(const LLWearableType::EType type,  	{  		// no null wearables please!  		LL_WARNS() << "Null wearable sent for type " << type << LL_ENDL; -		return MAX_CLOTHING_PER_TYPE;  	} -	if (type < LLWearableType::WT_COUNT || mWearableDatas[type].size() < MAX_CLOTHING_PER_TYPE) +	if (canAddWearable(type))  	{  		mWearableDatas[type].push_back(wearable);  		if (trigger_updated) @@ -110,9 +109,7 @@ U32 LLWearableData::pushWearable(const LLWearableType::EType type,  			const BOOL removed = FALSE;  			wearableUpdated(wearable, removed);  		} -		return mWearableDatas[type].size()-1;  	} -	return MAX_CLOTHING_PER_TYPE;  }  // virtual @@ -125,7 +122,7 @@ void LLWearableData::wearableUpdated(LLWearable *wearable, BOOL removed)  	}  } -void LLWearableData::popWearable(LLWearable *wearable) +void LLWearableData::eraseWearable(LLWearable *wearable)  {  	if (wearable == NULL)  	{ @@ -133,16 +130,16 @@ void LLWearableData::popWearable(LLWearable *wearable)  		return;  	} -	U32 index = getWearableIndex(wearable);  	const LLWearableType::EType type = wearable->getType(); -	if (index < MAX_CLOTHING_PER_TYPE && index < getWearableCount(type)) +	U32 index; +	if (getWearableIndex(wearable,index))  	{ -		popWearable(type, index); +		eraseWearable(type, index);  	}  } -void LLWearableData::popWearable(const LLWearableType::EType type, U32 index) +void LLWearableData::eraseWearable(const LLWearableType::EType type, U32 index)  {  	LLWearable *wearable = getWearable(type, index);  	if (wearable) @@ -204,11 +201,11 @@ void LLWearableData::pullCrossWearableValues(const LLWearableType::EType type)  } -U32	LLWearableData::getWearableIndex(const LLWearable *wearable) const +BOOL LLWearableData::getWearableIndex(const LLWearable *wearable, U32& index_found) const  {  	if (wearable == NULL)  	{ -		return MAX_CLOTHING_PER_TYPE; +		return FALSE;  	}  	const LLWearableType::EType type = wearable->getType(); @@ -216,18 +213,50 @@ U32	LLWearableData::getWearableIndex(const LLWearable *wearable) const  	if (wearable_iter == mWearableDatas.end())  	{  		LL_WARNS() << "tried to get wearable index with an invalid type!" << LL_ENDL; -		return MAX_CLOTHING_PER_TYPE; +		return FALSE;  	}  	const wearableentry_vec_t& wearable_vec = wearable_iter->second;  	for(U32 index = 0; index < wearable_vec.size(); index++)  	{  		if (wearable_vec[index] == wearable)  		{ -			return index; +			index_found = index; +			return TRUE;  		}  	} -	return MAX_CLOTHING_PER_TYPE; +	return FALSE; +} + +U32 LLWearableData::getClothingLayerCount() const +{ +	U32 count = 0; +	for (S32 i = 0; i < LLWearableType::WT_COUNT; i++) +	{ +		LLWearableType::EType type = (LLWearableType::EType)i; +		if (LLWearableType::getAssetType(type)==LLAssetType::AT_CLOTHING) +		{ +			count += getWearableCount(type); +		} +	} +	return count; +} + +BOOL LLWearableData::canAddWearable(const LLWearableType::EType type) const +{ +	LLAssetType::EType a_type = LLWearableType::getAssetType(type); +	if (a_type==LLAssetType::AT_CLOTHING) +	{ +		return (getClothingLayerCount() < MAX_CLOTHING_LAYERS); +	} +	else if (a_type==LLAssetType::AT_BODYPART) +	{ +		return (getWearableCount(type) < 1); +	} +	else +	{ +		return FALSE; +	}  }  BOOL LLWearableData::isOnTop(LLWearable* wearable) const diff --git a/indra/llappearance/llwearabledata.h b/indra/llappearance/llwearabledata.h index 03bd179f25..a0c446ea9e 100644..100755 --- a/indra/llappearance/llwearabledata.h +++ b/indra/llappearance/llwearabledata.h @@ -60,11 +60,13 @@ public:  	const LLWearable*	getBottomWearable(const LLWearableType::EType type) const;  	U32				getWearableCount(const LLWearableType::EType type) const;  	U32				getWearableCount(const U32 tex_index) const; -	U32				getWearableIndex(const LLWearable *wearable) const; +	BOOL			getWearableIndex(const LLWearable *wearable, U32& index) const; +	U32				getClothingLayerCount() const; +	BOOL			canAddWearable(const LLWearableType::EType type) const;  	BOOL			isOnTop(LLWearable* wearable) const; - -	static const U32 MAX_CLOTHING_PER_TYPE = 5;  +	 +	static const U32 MAX_CLOTHING_LAYERS = 60;  	//--------------------------------------------------------------------  	// Setters @@ -72,11 +74,11 @@ public:  protected:  	// Low-level data structure setter - public access is via setWearableItem, etc.  	void 			setWearable(const LLWearableType::EType type, U32 index, LLWearable *wearable); -	U32 			pushWearable(const LLWearableType::EType type, LLWearable *wearable,  +	void 			pushWearable(const LLWearableType::EType type, LLWearable *wearable,   								 bool trigger_updated = true);  	virtual void	wearableUpdated(LLWearable *wearable, BOOL removed); -	void 			popWearable(LLWearable *wearable); -	void			popWearable(const LLWearableType::EType type, U32 index); +	void 			eraseWearable(LLWearable *wearable); +	void			eraseWearable(const LLWearableType::EType type, U32 index);  	void			clearWearableType(const LLWearableType::EType type);  	bool			swapWearables(const LLWearableType::EType type, U32 index_a, U32 index_b); diff --git a/indra/llappearance/llwearabletype.cpp b/indra/llappearance/llwearabletype.cpp index 618e2a1941..87109a5906 100644..100755 --- a/indra/llappearance/llwearabletype.cpp +++ b/indra/llappearance/llwearabletype.cpp @@ -27,6 +27,7 @@  #include "linden_common.h"  #include "llwearabletype.h"  #include "llinventorytype.h" +#include "llinventorydefines.h"  static LLTranslationBridge* sTrans = NULL; @@ -160,7 +161,7 @@ BOOL LLWearableType::getDisableCameraSwitch(LLWearableType::EType type)  	return entry->mDisableCameraSwitch;  } -// static  +// static  BOOL LLWearableType::getAllowMultiwear(LLWearableType::EType type)  {  	const LLWearableDictionary *dict = LLWearableDictionary::getInstance(); @@ -169,3 +170,9 @@ BOOL LLWearableType::getAllowMultiwear(LLWearableType::EType type)  	return entry->mAllowMultiwear;  } +// static +LLWearableType::EType LLWearableType::inventoryFlagsToWearableType(U32 flags) +{ +    return  (LLWearableType::EType)(flags & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK); +} + diff --git a/indra/llappearance/llwearabletype.h b/indra/llappearance/llwearabletype.h index 7c9594644d..519d5b92a2 100644..100755 --- a/indra/llappearance/llwearabletype.h +++ b/indra/llappearance/llwearabletype.h @@ -80,6 +80,7 @@ public:  	static LLInventoryType::EIconName 	getIconName(EType type);  	static BOOL 						getDisableCameraSwitch(EType type);  	static BOOL 						getAllowMultiwear(EType type); +    static EType						inventoryFlagsToWearableType(U32 flags);  protected:  	LLWearableType() {} diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 1459b9ada2..de5aa0fde4 100755 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -162,6 +162,7 @@ set(llcommon_HEADER_FILES      llhash.h      llheartbeat.h      llindexedvector.h +    llinitdestroyclass.h      llinitparam.h      llinstancetracker.h      llkeythrottle.h @@ -178,6 +179,7 @@ set(llcommon_HEADER_FILES      llmortician.h      llnametable.h      llpointer.h +    llpounceable.h      llpredicate.h      llpreprocessor.h      llpriqueuemap.h @@ -310,6 +312,7 @@ if (LL_TESTS)    LL_ADD_INTEGRATION_TEST(llprocess "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")    LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}") +  LL_ADD_INTEGRATION_TEST(llpounceable "" "${test_libs}")    # *TODO - reenable these once tcmalloc libs no longer break the build.    #ADD_BUILD_TEST(llallocator llcommon) diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 5a40845e7d..2c52b11594 100755 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -48,6 +48,7 @@  #include "lleventtimer.h"  #include "google_breakpad/exception_handler.h"  #include "stringize.h" +#include "llcleanup.h"  //  // Signal handling @@ -177,7 +178,7 @@ LLApp::~LLApp()  	if(mExceptionHandler != 0) delete mExceptionHandler; -	LLCommon::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLCommon);  }  // static diff --git a/indra/llcommon/llcleanup.h b/indra/llcommon/llcleanup.h new file mode 100644 index 0000000000..8eda9a7fb3 --- /dev/null +++ b/indra/llcommon/llcleanup.h @@ -0,0 +1,30 @@ +/** + * @file   llcleanup.h + * @author Nat Goodspeed + * @date   2015-05-20 + * @brief  Mechanism for cleaning up subsystem resources + *  + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Copyright (c) 2015, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLCLEANUP_H) +#define LL_LLCLEANUP_H + +#include "llerror.h" + +// Instead of directly calling SomeClass::cleanupClass(), use +// SUBSYSTEM_CLEANUP(SomeClass); +// This logs the call as well as performing it. That gives us a baseline +// subsystem shutdown order against which to compare subsequent dynamic +// shutdown schemes. +#define SUBSYSTEM_CLEANUP(CLASSNAME)                                    \ +    do {                                                                \ +        LL_INFOS("Cleanup") << "Calling " #CLASSNAME "::cleanupClass()" << LL_ENDL; \ +        CLASSNAME::cleanupClass();                                      \ +    } while (0) +// Use ancient do { ... } while (0) macro trick to permit a block of +// statements with the same syntax as a single statement. + +#endif /* ! defined(LL_LLCLEANUP_H) */ diff --git a/indra/llcommon/llcommon.cpp b/indra/llcommon/llcommon.cpp index 19642b0982..439ff4e628 100755 --- a/indra/llcommon/llcommon.cpp +++ b/indra/llcommon/llcommon.cpp @@ -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);  } diff --git a/indra/llcommon/llerror.cpp b/indra/llcommon/llerror.cpp index f2c647c2d9..b662a2c2be 100755 --- a/indra/llcommon/llerror.cpp +++ b/indra/llcommon/llerror.cpp @@ -413,6 +413,11 @@ namespace  namespace LLError  { +	bool is_available() +	{ +		return Globals::instanceExists(); +	} +  	class SettingsConfig : public LLRefCount  	{  		friend class Settings; diff --git a/indra/llcommon/llerrorcontrol.h b/indra/llcommon/llerrorcontrol.h index 56ac52e5de..56e84f7172 100755 --- a/indra/llcommon/llerrorcontrol.h +++ b/indra/llcommon/llerrorcontrol.h @@ -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 diff --git a/indra/llcommon/llinitdestroyclass.h b/indra/llcommon/llinitdestroyclass.h new file mode 100644 index 0000000000..49bcefc33d --- /dev/null +++ b/indra/llcommon/llinitdestroyclass.h @@ -0,0 +1,190 @@ +/** + * @file   llinitdestroyclass.h + * @author Nat Goodspeed + * @date   2015-05-27 + * @brief  LLInitClass / LLDestroyClass mechanism + * + * The LLInitClass template, extracted from llui.h, ensures that control will + * reach a static initClass() method. LLDestroyClass does the same for a + * static destroyClass() method. + * + * The distinguishing characteristics of these templates are: + * + * - All LLInitClass<T>::initClass() methods are triggered by an explicit call + *   to LLInitClassList::instance().fireCallbacks(). Presumably this call + *   happens sometime after all static objects in the program have been + *   initialized. In other words, each LLInitClass<T>::initClass() method + *   should be able to make some assumptions about global program state. + * + * - Similarly, LLDestroyClass<T>::destroyClass() methods are triggered by + *   LLDestroyClassList::instance().fireCallbacks(). Again, presumably this + *   happens at a well-defined moment in the program's shutdown sequence. + * + * - The initClass() calls happen in an unspecified sequence. You may not rely + *   on the relative ordering of LLInitClass<T>::initClass() versus another + *   LLInitClass<U>::initClass() method. If you need such a guarantee, use + *   LLSingleton instead and make the dependency explicit. + * + * - Similarly, LLDestroyClass<T>::destroyClass() may happen either before or + *   after LLDestroyClass<U>::destroyClass(). You cannot rely on that order. + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Copyright (c) 2015, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLINITDESTROYCLASS_H) +#define LL_LLINITDESTROYCLASS_H + +#include "llerror.h" +#include "llsingleton.h" +#include <boost/function.hpp> +#include <boost/signals2/signal.hpp> +#include <typeinfo> + +/** + * LLCallbackRegistry is an implementation detail base class for + * LLInitClassList and LLDestroyClassList. It's a very thin wrapper around a + * Boost.Signals2 signal object. + */ +class LLCallbackRegistry +{ +public: +	typedef boost::signals2::signal<void()> callback_signal_t; +	 +	void registerCallback(const callback_signal_t::slot_type& slot) +	{ +		mCallbacks.connect(slot); +	} + +	void fireCallbacks() +	{ +		mCallbacks(); +	} + +private: +	callback_signal_t mCallbacks; +}; + +/** + * LLInitClassList is the LLCallbackRegistry for LLInitClass. It stores the + * registered initClass() methods. It must be an LLSingleton because + * LLInitClass registers its initClass() method at static construction time + * (before main()), requiring LLInitClassList to be fully constructed on + * demand regardless of module initialization order. + */ +class LLInitClassList :  +	public LLCallbackRegistry,  +	public LLSingleton<LLInitClassList> +{ +	friend class LLSingleton<LLInitClassList>; +private: +	LLInitClassList() {} +}; + +/** + * LLDestroyClassList is the LLCallbackRegistry for LLDestroyClass. It stores + * the registered destroyClass() methods. It must be an LLSingleton because + * LLDestroyClass registers its destroyClass() method at static construction + * time (before main()), requiring LLDestroyClassList to be fully constructed + * on demand regardless of module initialization order. + */ +class LLDestroyClassList :  +	public LLCallbackRegistry,  +	public LLSingleton<LLDestroyClassList> +{ +	friend class LLSingleton<LLDestroyClassList>; +private: +	LLDestroyClassList() {} +}; + +/** + * LLRegisterWith is an implementation detail for LLInitClass and + * LLDestroyClass. It is intended to be used as a static class member whose + * constructor registers the specified callback with the LLMumbleClassList + * singleton registry specified as the template argument. + */ +template<typename T> +class LLRegisterWith +{ +public: +	LLRegisterWith(boost::function<void ()> func) +	{ +		T::instance().registerCallback(func); +	} + +	// this avoids a MSVC bug where non-referenced static members are "optimized" away +	// even if their constructors have side effects +	S32 reference() +	{ +		S32 dummy; +		dummy = 0; +		return dummy; +	} +}; + +/** + * Derive MyClass from LLInitClass<MyClass> (the Curiously Recurring Template + * Pattern) to ensure that the static method MyClass::initClass() will be + * called (along with all other LLInitClass<T> subclass initClass() methods) + * when someone calls LLInitClassList::instance().fireCallbacks(). This gives + * the application specific control over the timing of all such + * initializations, without having to insert calls for every such class into + * generic application code. + */ +template<typename T> +class LLInitClass +{ +public: +	LLInitClass() { sRegister.reference(); } + +	// When this static member is initialized, the subclass initClass() method +	// is registered on LLInitClassList. See sRegister definition below. +	static LLRegisterWith<LLInitClassList> sRegister; +private: + +	// Provide a default initClass() method in case subclass misspells (or +	// omits) initClass(). This turns a potential build error into a fatal +	// runtime error. +	static void initClass() +	{ +		LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL; +	} +}; + +/** + * Derive MyClass from LLDestroyClass<MyClass> (the Curiously Recurring + * Template Pattern) to ensure that the static method MyClass::destroyClass() + * will be called (along with other LLDestroyClass<T> subclass destroyClass() + * methods) when someone calls LLDestroyClassList::instance().fireCallbacks(). + * This gives the application specific control over the timing of all such + * cleanup calls, without having to insert calls for every such class into + * generic application code. + */ +template<typename T> +class LLDestroyClass +{ +public: +	LLDestroyClass() { sRegister.reference(); } + +	// When this static member is initialized, the subclass destroyClass() +	// method is registered on LLInitClassList. See sRegister definition +	// below. +	static LLRegisterWith<LLDestroyClassList> sRegister; +private: + +	// Provide a default destroyClass() method in case subclass misspells (or +	// omits) destroyClass(). This turns a potential build error into a fatal +	// runtime error. +	static void destroyClass() +	{ +		LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL; +	} +}; + +// Here's where LLInitClass<T> specifies the subclass initClass() method. +template <typename T> LLRegisterWith<LLInitClassList> LLInitClass<T>::sRegister(&T::initClass); +// Here's where LLDestroyClass<T> specifies the subclass destroyClass() method. +template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sRegister(&T::destroyClass); + +#endif /* ! defined(LL_LLINITDESTROYCLASS_H) */ diff --git a/indra/llcommon/llpounceable.h b/indra/llcommon/llpounceable.h new file mode 100644 index 0000000000..77b711bdc6 --- /dev/null +++ b/indra/llcommon/llpounceable.h @@ -0,0 +1,215 @@ +/** + * @file   llpounceable.h + * @author Nat Goodspeed + * @date   2015-05-22 + * @brief  LLPounceable is tangentially related to a future: it's a holder for + *         a value that may or may not exist yet. Unlike a future, though, + *         LLPounceable freely allows reading the held value. (If the held + *         type T does not have a distinguished "empty" value, consider using + *         LLPounceable<boost::optional<T>>.) + * + *         LLPounceable::callWhenReady() is this template's claim to fame. It + *         allows its caller to "pounce" on the held value as soon as it + *         becomes non-empty. Call callWhenReady() with any C++ callable + *         accepting T. If the held value is already non-empty, callWhenReady() + *         will immediately call the callable with the held value. If the held + *         value is empty, though, callWhenReady() will enqueue the callable + *         for later. As soon as LLPounceable is assigned a non-empty held + *         value, it will flush the queue of deferred callables. + * + *         Consider a global LLMessageSystem* gMessageSystem. Message system + *         initialization happens at a very specific point during viewer + *         initialization. Other subsystems want to register callbacks on the + *         LLMessageSystem instance as soon as it's initialized, but their own + *         initialization may precede that. If we define gMessageSystem to be + *         an LLPounceable<LLMessageSystem*>, a subsystem can use + *         callWhenReady() to either register immediately (if gMessageSystem + *         is already up and runnning) or register as soon as gMessageSystem + *         is set with a new, initialized instance. + * + * $LicenseInfo:firstyear=2015&license=viewerlgpl$ + * Copyright (c) 2015, Linden Research, Inc. + * $/LicenseInfo$ + */ + +#if ! defined(LL_LLPOUNCEABLE_H) +#define LL_LLPOUNCEABLE_H + +#include "llsingleton.h" +#include <boost/noncopyable.hpp> +#include <boost/call_traits.hpp> +#include <boost/type_traits/remove_pointer.hpp> +#include <boost/utility/value_init.hpp> +#include <boost/unordered_map.hpp> +#include <boost/signals2/signal.hpp> + +// Forward declare the user template, since we want to be able to point to it +// in some of its implementation classes. +template <typename T, class TAG> +class LLPounceable; + +template <typename T, typename TAG> +struct LLPounceableTraits +{ +    // Our "queue" is a signal object with correct signature. +    typedef boost::signals2::signal<void (typename boost::call_traits<T>::param_type)> signal_t; +    // Call callWhenReady() with any callable accepting T. +    typedef typename signal_t::slot_type func_t; +    // owner pointer type +    typedef LLPounceable<T, TAG>* owner_ptr; +}; + +// Tag types distinguish the two different implementations of LLPounceable's +// queue. +struct LLPounceableQueue {}; +struct LLPounceableStatic {}; + +// generic LLPounceableQueueImpl deliberately omitted: only the above tags are +// legal +template <typename T, class TAG> +class LLPounceableQueueImpl; + +// The implementation selected by LLPounceableStatic uses an LLSingleton +// because we can't count on a data member queue being initialized at the time +// we start getting callWhenReady() calls. This is that LLSingleton. +template <typename T> +class LLPounceableQueueSingleton: +    public LLSingleton<LLPounceableQueueSingleton<T> > +{ +private: +    typedef LLPounceableTraits<T, LLPounceableStatic> traits; +    typedef typename traits::owner_ptr owner_ptr; +    typedef typename traits::signal_t signal_t; + +    // For a given held type T, every LLPounceable<T, LLPounceableStatic> +    // instance will call on the SAME LLPounceableQueueSingleton instance -- +    // given how class statics work. We must keep a separate queue for each +    // LLPounceable instance. Use a hash map for that. +    typedef boost::unordered_map<owner_ptr, signal_t> map_t; + +public: +    // Disambiguate queues belonging to different LLPounceables. +    signal_t& get(owner_ptr owner) +    { +        // operator[] has find-or-create semantics -- just what we want! +        return mMap[owner]; +    } + +private: +    map_t mMap; +}; + +// LLPounceableQueueImpl that uses the above LLSingleton +template <typename T> +class LLPounceableQueueImpl<T, LLPounceableStatic> +{ +public: +    typedef LLPounceableTraits<T, LLPounceableStatic> traits; +    typedef typename traits::owner_ptr owner_ptr; +    typedef typename traits::signal_t signal_t; + +    signal_t& get(owner_ptr owner) const +    { +        // this Impl contains nothing; it delegates to the Singleton +        return LLPounceableQueueSingleton<T>::instance().get(owner); +    } +}; + +// The implementation selected by LLPounceableQueue directly contains the +// queue of interest, suitable for an LLPounceable we can trust to be fully +// initialized when it starts getting callWhenReady() calls. +template <typename T> +class LLPounceableQueueImpl<T, LLPounceableQueue> +{ +public: +    typedef LLPounceableTraits<T, LLPounceableQueue> traits; +    typedef typename traits::owner_ptr owner_ptr; +    typedef typename traits::signal_t signal_t; + +    signal_t& get(owner_ptr) +    { +        return mQueue; +    } + +private: +    signal_t mQueue; +}; + +// LLPounceable<T> is for an LLPounceable instance on the heap or the stack. +// LLPounceable<T, LLPounceableStatic> is for a static LLPounceable instance. +template <typename T, class TAG=LLPounceableQueue> +class LLPounceable: public boost::noncopyable +{ +private: +    typedef LLPounceableTraits<T, TAG> traits; +    typedef typename traits::owner_ptr owner_ptr; +    typedef typename traits::signal_t signal_t; + +public: +    typedef typename traits::func_t func_t; + +    // By default, both the initial value and the distinguished empty value +    // are a default-constructed T instance. However you can explicitly +    // specify each. +    LLPounceable(typename boost::call_traits<T>::value_type init =boost::value_initialized<T>(), +                 typename boost::call_traits<T>::param_type empty=boost::value_initialized<T>()): +        mHeld(init), +        mEmpty(empty) +    {} + +    // make read access to mHeld as cheap and transparent as possible +    operator T () const { return mHeld; } +    typename boost::remove_pointer<T>::type operator*() const { return *mHeld; } +    typename boost::call_traits<T>::value_type operator->() const { return mHeld; } +    // uncomment 'explicit' as soon as we allow C++11 compilation +    /*explicit*/ operator bool() const { return bool(mHeld); } +    bool operator!() const { return ! mHeld; } + +    // support both assignment (dumb ptr idiom) and reset() (smart ptr) +    void operator=(typename boost::call_traits<T>::param_type value) +    { +        reset(value); +    } + +    void reset(typename boost::call_traits<T>::param_type value) +    { +        mHeld = value; +        // If this new value is non-empty, flush anything pending in the queue. +        if (mHeld != mEmpty) +        { +            signal_t& signal(get_signal()); +            signal(mHeld); +            signal.disconnect_all_slots(); +        } +    } + +    // our claim to fame +    void callWhenReady(const func_t& func) +    { +        if (mHeld != mEmpty) +        { +            // If the held value is already non-empty, immediately call func() +            func(mHeld); +        } +        else +        { +            // Held value still empty, queue func() for later. By default, +            // connect() enqueues slots in FIFO order. +            get_signal().connect(func); +        } +    } + +private: +    signal_t& get_signal() { return mQueue.get(this); } + +    // Store both the current and the empty value. +    // MAYBE: Might be useful to delegate to LLPounceableTraits the meaning of +    // testing for "empty." For some types we want operator!(); for others we +    // want to compare to a distinguished value. +    typename boost::call_traits<T>::value_type mHeld, mEmpty; +    // This might either contain the queue (LLPounceableQueue) or delegate to +    // an LLSingleton (LLPounceableStatic). +    LLPounceableQueueImpl<T, TAG> mQueue; +}; + +#endif /* ! defined(LL_LLPOUNCEABLE_H) */ diff --git a/indra/llcommon/llregistry.h b/indra/llcommon/llregistry.h index 29950c108d..fde729f8f9 100755 --- a/indra/llcommon/llregistry.h +++ b/indra/llcommon/llregistry.h @@ -269,7 +269,7 @@ public:  		~ScopedRegistrar()  		{ -			if (!singleton_t::destroyed()) +			if (singleton_t::instanceExists())  			{  				popScope();  			} diff --git a/indra/llcommon/llsingleton.cpp b/indra/llcommon/llsingleton.cpp index 9b49e52377..2813814ae1 100755 --- a/indra/llcommon/llsingleton.cpp +++ b/indra/llcommon/llsingleton.cpp @@ -25,7 +25,326 @@   */  #include "linden_common.h" -  #include "llsingleton.h" +#include "llerror.h" +#include "llerrorcontrol.h"         // LLError::is_available() +#include "lldependencies.h" +#include <boost/foreach.hpp> +#include <algorithm> +#include <iostream>                 // std::cerr in dire emergency +#include <sstream> +#include <stdexcept> + +// Our master list of all LLSingletons is itself an LLSingleton. We used to +// store it in a function-local static, but that could get destroyed before +// the last of the LLSingletons -- and ~LLSingletonBase() definitely wants to +// remove itself from the master list. Since the whole point of this master +// list is to help track inter-LLSingleton dependencies, and since we have +// this implicit dependency from every LLSingleton to the master list, make it +// an LLSingleton. +class LLSingletonBase::MasterList: +    public LLSingleton<LLSingletonBase::MasterList> +{ +private: +    friend class LLSingleton<LLSingletonBase::MasterList>; + +public: +    // No need to make this private with accessors; nobody outside this source +    // file can see it. +    LLSingletonBase::list_t mList; +}; + +//static +LLSingletonBase::list_t& LLSingletonBase::get_master() +{ +    return LLSingletonBase::MasterList::instance().mList; +} + +void LLSingletonBase::add_master() +{ +    // As each new LLSingleton is constructed, add to the master list. +    get_master().push_back(this); +} + +void LLSingletonBase::remove_master() +{ +    // When an LLSingleton is destroyed, remove from master list. +    // add_master() used to capture the iterator to the newly-added list item +    // so we could directly erase() it from the master list. Unfortunately +    // that runs afoul of destruction-dependency order problems. So search the +    // master list, and remove this item IF FOUND. We have few enough +    // LLSingletons, and they are so rarely destroyed (once per run), that the +    // cost of a linear search should not be an issue. +    get_master().remove(this); +} + +// Wrapping our initializing list in a static method ensures that it will be +// constructed on demand. This list doesn't also need to be in an LLSingleton +// because (a) it should be empty by program shutdown and (b) none of our +// destructors reference it. +//static +LLSingletonBase::list_t& LLSingletonBase::get_initializing() +{ +    static list_t sList; +    return sList; +} + +LLSingletonBase::LLSingletonBase(): +    mCleaned(false), +    mDeleteSingleton(NULL) +{ +    // Make this the currently-initializing LLSingleton. +    push_initializing(); +} + +LLSingletonBase::~LLSingletonBase() {} + +void LLSingletonBase::push_initializing() +{ +    get_initializing().push_back(this); +} + +void LLSingletonBase::pop_initializing() +{ +    list_t& list(get_initializing()); +    if (list.empty()) +    { +        logerrs("Underflow in stack of currently-initializing LLSingletons at ", +                typeid(*this).name(), "::getInstance()"); +    } +    if (list.back() != this) +    { +        logerrs("Push/pop mismatch in stack of currently-initializing LLSingletons: ", +                typeid(*this).name(), "::getInstance() trying to pop ", +                typeid(*list.back()).name()); +    } +    // Here we're sure that list.back() == this. Whew, pop it. +    list.pop_back(); +} + +void LLSingletonBase::capture_dependency(EInitState initState) +{ +    // Did this getInstance() call come from another LLSingleton, or from +    // vanilla application code? Note that although this is a nontrivial +    // method, the vast majority of its calls arrive here with initializing +    // empty(). +    list_t& initializing(get_initializing()); +    if (! initializing.empty()) +    { +        // getInstance() is being called by some other LLSingleton. But -- is +        // this a circularity? That is, does 'this' already appear in the +        // initializing stack? +        // For what it's worth, normally 'initializing' should contain very +        // few elements. +        list_t::const_iterator found = +            std::find(initializing.begin(), initializing.end(), this); +        if (found != initializing.end()) +        { +            // Report the circularity. Requiring the coder to dig through the +            // logic to diagnose exactly how we got here is less than helpful. +            std::ostringstream out; +            for ( ; found != initializing.end(); ++found) +            { +                // 'found' is an iterator; *found is an LLSingletonBase*; **found +                // is the actual LLSingletonBase instance. +                out << typeid(**found).name() << " -> "; +            } +            // We promise to capture dependencies from both the constructor +            // and the initSingleton() method, so an LLSingleton's instance +            // pointer is on the initializing list during both. Now that we've +            // detected circularity, though, we must distinguish the two. If +            // the recursive call is from the constructor, we CAN'T honor it: +            // otherwise we'd be returning a pointer to a partially- +            // constructed object! But from initSingleton() is okay: that +            // method exists specifically to support circularity. +            // Decide which log helper to call based on initState. They have +            // identical signatures. +            ((initState == CONSTRUCTING)? logerrs : logwarns) +                ("LLSingleton circularity: ", out.str().c_str(), typeid(*this).name(), ""); +        } +        else +        { +            // Here 'this' is NOT already in the 'initializing' stack. Great! +            // Record the dependency. +            // initializing.back() is the LLSingletonBase* currently being +            // initialized. Store 'this' in its mDepends set. +            initializing.back()->mDepends.insert(this); +        } +    } +} + +//static +LLSingletonBase::vec_t LLSingletonBase::dep_sort() +{ +    // While it would theoretically be possible to maintain a static +    // SingletonDeps through the life of the program, dynamically adding and +    // removing LLSingletons as they are created and destroyed, in practice +    // it's less messy to construct it on demand. The overhead of doing so +    // should happen basically twice: once for cleanupAll(), once for +    // deleteAll(). +    typedef LLDependencies<LLSingletonBase*> SingletonDeps; +    SingletonDeps sdeps; +    list_t& master(get_master()); +    BOOST_FOREACH(LLSingletonBase* sp, master) +    { +        // Build the SingletonDeps structure by adding, for each +        // LLSingletonBase* sp in the master list, sp itself. It has no +        // associated value type in our SingletonDeps, hence the 0. We don't +        // record the LLSingletons it must follow; rather, we record the ones +        // it must precede. Copy its mDepends to a KeyList to express that. +        sdeps.add(sp, 0, +                  SingletonDeps::KeyList(), +                  SingletonDeps::KeyList(sp->mDepends.begin(), sp->mDepends.end())); +    } +    vec_t ret; +    ret.reserve(master.size()); +    // We should be able to effect this with a transform_iterator that +    // extracts just the first (key) element from each sorted_iterator, then +    // uses vec_t's range constructor... but frankly this is more +    // straightforward, as long as we remember the above reserve() call! +    BOOST_FOREACH(SingletonDeps::sorted_iterator::value_type pair, sdeps.sort()) +    { +        ret.push_back(pair.first); +    } +    // The master list is not itself pushed onto the master list. Add it as +    // the very last entry -- it is the LLSingleton on which ALL others +    // depend! -- so our caller will process it. +    ret.push_back(MasterList::getInstance()); +    return ret; +} + +//static +void LLSingletonBase::cleanupAll() +{ +    // It's essential to traverse these in dependency order. +    BOOST_FOREACH(LLSingletonBase* sp, dep_sort()) +    { +        // Call cleanupSingleton() only if we haven't already done so for this +        // instance. +        if (! sp->mCleaned) +        { +            sp->mCleaned = true; + +            try +            { +                sp->cleanupSingleton(); +            } +            catch (const std::exception& e) +            { +                logwarns("Exception in ", typeid(*sp).name(), +                         "::cleanupSingleton(): ", e.what()); +            } +            catch (...) +            { +                logwarns("Unknown exception in ", typeid(*sp).name(), +                         "::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 char* name = typeid(*sp).name(); +        try +        { +            // Call static method through instance function pointer. +            if (! sp->mDeleteSingleton) +            { +                // This Should Not Happen... but carry on. +                logwarns(name, "::mDeleteSingleton not initialized!"); +            } +            else +            { +                // properly initialized: call it. +                // From this point on, DO NOT DEREFERENCE sp! +                sp->mDeleteSingleton(); +            } +        } +        catch (const std::exception& e) +        { +            logwarns("Exception in ", name, "::deleteSingleton(): ", e.what()); +        } +        catch (...) +        { +            logwarns("Unknown exception in ", name, "::deleteSingleton()"); +        } +    } +} + +/*------------------------ Final cleanup management ------------------------*/ +class LLSingletonBase::MasterRefcount +{ +public: +    // store a POD int so it will be statically initialized to 0 +    int refcount; +}; +static LLSingletonBase::MasterRefcount sMasterRefcount; + +LLSingletonBase::ref_ptr_t LLSingletonBase::get_master_refcount() +{ +    // Calling this method constructs a new ref_ptr_t, which implicitly calls +    // intrusive_ptr_add_ref(MasterRefcount*). +    return &sMasterRefcount; +} + +void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount* mrc) +{ +    // Count outstanding SingletonLifetimeManager instances. +    ++mrc->refcount; +} + +void intrusive_ptr_release(LLSingletonBase::MasterRefcount* mrc) +{ +    // Notice when each SingletonLifetimeManager instance is destroyed. +    if (! --mrc->refcount) +    { +        // The last instance was destroyed. Time to kill any remaining +        // LLSingletons -- but in dependency order. +        LLSingletonBase::deleteAll(); +    } +} + +/*---------------------------- Logging helpers -----------------------------*/ +//static +void LLSingletonBase::logerrs(const char* p1, const char* p2, const char* p3, const char* p4) +{ +    // Check LLError::is_available() because some of LLError's infrastructure +    // is itself an LLSingleton. If that LLSingleton has not yet been +    // initialized, trying to log will engage LLSingleton machinery... and +    // around and around we go. +    if (LLError::is_available()) +    { +        LL_ERRS() << p1 << p2 << p3 << p4 << LL_ENDL; +    } +    else +    { +        // Caller may be a test program, or something else whose stderr is +        // visible to the user. +        std::cerr << p1 << p2 << p3 << p4 << std::endl; +        // The other important side effect of LL_ERRS() is +        // https://www.youtube.com/watch?v=OMG7paGJqhQ (emphasis on OMG) +        LLError::crashAndLoop(std::string()); +    } +} +//static +void LLSingletonBase::logwarns(const char* p1, const char* p2, const char* p3, const char* p4) +{ +    // See logerrs() remarks about is_available(). +    if (LLError::is_available()) +    { +        LL_WARNS() << p1 << p2 << p3 << p4 << LL_ENDL; +    } +    else +    { +        std::cerr << p1 << p2 << p3 << p4 << std::endl; +    } +} diff --git a/indra/llcommon/llsingleton.h b/indra/llcommon/llsingleton.h index 6e6291a165..a82101c367 100755 --- a/indra/llcommon/llsingleton.h +++ b/indra/llcommon/llsingleton.h @@ -25,185 +25,385 @@  #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> + +// TODO: +// Tests for all this! +class LLSingletonBase: private boost::noncopyable +{ +public: +    class MasterList; +    class MasterRefcount; +    typedef boost::intrusive_ptr<MasterRefcount> ref_ptr_t; + +private: +    // All existing LLSingleton instances are tracked in this master list. +    typedef std::list<LLSingletonBase*> list_t; +    static list_t& get_master(); +    // This, on the other hand, is a stack whose top indicates the LLSingleton +    // currently being initialized. +    static list_t& get_initializing(); +    // Produce a vector<LLSingletonBase*> of master list, in dependency order. +    typedef std::vector<LLSingletonBase*> vec_t; +    static vec_t dep_sort(); + +    bool mCleaned;                  // cleanupSingleton() has been called +    // we directly depend on these other LLSingletons +    typedef boost::unordered_set<LLSingletonBase*> set_t; +    set_t mDepends; + +protected: +    typedef enum e_init_state +    { +        UNINITIALIZED = 0,          // must be default-initialized state +        CONSTRUCTING, +        INITIALIZING, +        INITIALIZED, +        DELETED +    } EInitState; + +    // Base-class constructor should only be invoked by the DERIVED_TYPE +    // constructor. +    LLSingletonBase(); +    virtual ~LLSingletonBase(); + +    // Every new LLSingleton should be added to/removed from the master list +    void add_master(); +    void remove_master(); +    // with a little help from our friends. +    template <class T> friend struct LLSingleton_manage_master; + +    // Maintain a stack of the LLSingleton subclass instance currently being +    // initialized. We use this to notice direct dependencies: we want to know +    // if A requires B. We deduce that if while initializing A, control +    // reaches B::getInstance(). +    // We want &A to be at the top of that stack during both A::A() and +    // A::initSingleton(), since a call to B::getInstance() might occur during +    // either. +    // Unfortunately the desired timespan does not correspond neatly with a +    // single C++ scope, else we'd use RAII to track it. But we do know that +    // LLSingletonBase's constructor definitely runs just before +    // LLSingleton's, which runs just before the specific subclass's. +    void push_initializing(); +    // LLSingleton is, and must remain, the only caller to initSingleton(). +    // That being the case, we control exactly when it happens -- and we can +    // pop the stack immediately thereafter. +    void pop_initializing(); +    // If a given call to B::getInstance() happens during either A::A() or +    // A::initSingleton(), record that A directly depends on B. +    void capture_dependency(EInitState); + +    // delegate LL_ERRS() logging to llsingleton.cpp +    static void logerrs(const char* p1, const char* p2="", +                        const char* p3="", const char* p4=""); +    // delegate LL_WARNS() logging to llsingleton.cpp +    static void logwarns(const char* p1, const char* p2="", +                         const char* p3="", const char* p4=""); + +    // 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)(); -// 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. +public: +    /** +     * Call this to call the cleanupSingleton() method for every LLSingleton +     * constructed since the start of the last cleanupAll() call. (Any +     * LLSingleton constructed DURING a cleanupAll() call won't be cleaned up +     * until the next cleanupAll() call.) cleanupSingleton() neither deletes +     * nor destroys its LLSingleton; therefore it's safe to include logic that +     * might take significant realtime or even throw an exception. +     * +     * The most important property of cleanupAll() is that cleanupSingleton() +     * methods are called in dependency order, leaf classes last. Thus, given +     * two LLSingleton subclasses A and B, if A's dependency on B is properly +     * expressed as a B::getInstance() or B::instance() call during either +     * A::A() or A::initSingleton(), B will be cleaned up after A. +     * +     * If a cleanupSingleton() method throws an exception, the exception is +     * logged, but cleanupAll() attempts to continue calling the rest of the +     * cleanupSingleton() methods. +     */ +    static void cleanupAll(); +    /** +     * Call this to call the deleteSingleton() method for every LLSingleton +     * constructed since the start of the last deleteAll() call. (Any +     * LLSingleton constructed DURING a deleteAll() call won't be cleaned up +     * until the next deleteAll() call.) deleteSingleton() deletes and +     * destroys its LLSingleton. Any cleanup logic that might take significant +     * realtime -- or throw an exception -- must not be placed in your +     * LLSingleton's destructor, but rather in its cleanupSingleton() method. +     * +     * The most important property of deleteAll() is that deleteSingleton() +     * methods are called in dependency order, leaf classes last. Thus, given +     * two LLSingleton subclasses A and B, if A's dependency on B is properly +     * expressed as a B::getInstance() or B::instance() call during either +     * A::A() or A::initSingleton(), B will be cleaned up after A. +     * +     * If a deleteSingleton() method throws an exception, the exception is +     * logged, but deleteAll() attempts to continue calling the rest of the +     * deleteSingleton() methods. +     */ +    static void deleteAll(); +}; + +// support ref_ptr_t +void intrusive_ptr_add_ref(LLSingletonBase::MasterRefcount*); +void intrusive_ptr_release(LLSingletonBase::MasterRefcount*); + +// Most of the time, we want LLSingleton_manage_master() to forward its +// methods to LLSingletonBase::add_master() and remove_master(). +template <class T> +struct LLSingleton_manage_master +{ +    void add(LLSingletonBase* sb) { sb->add_master(); } +    void remove(LLSingletonBase* sb) { sb->remove_master(); } +}; + +// But for the specific case of LLSingletonBase::MasterList, don't. +template <> +struct LLSingleton_manage_master<LLSingletonBase::MasterList> +{ +    void add(LLSingletonBase*) {} +    void remove(LLSingletonBase*) {} +}; +/** + * LLSingleton implements the getInstance() method part of the Singleton + * pattern. It can't make the derived class constructors protected, though, so + * you have to do that yourself. + * + * Derive your class from LLSingleton, passing your subclass name as + * LLSingleton's template parameter, like so: + * + *   class Foo: public LLSingleton<Foo>{}; + * + *   Foo& instance = Foo::instance(); + * + * LLSingleton recognizes a couple special methods in your derived class. + * + * If you override LLSingleton<T>::initSingleton(), your method will be called + * immediately after the instance is constructed. This is useful for breaking + * circular dependencies: if you find that your LLSingleton subclass + * constructor references other LLSingleton subclass instances in a chain + * leading back to yours, move the instance reference from your constructor to + * your initSingleton() method. + * + * If you override LLSingleton<T>::cleanupSingleton(), your method will be + * called if someone calls LLSingletonBase::cleanupAll(). The significant part + * of this promise is that cleanupAll() will call individual + * cleanupSingleton() methods in reverse dependency order. + * + * That is, consider LLSingleton subclasses C, B and A. A depends on B, which + * in turn depends on C. These dependencies are expressed as calls to + * B::instance() or B::getInstance(), and C::instance() or C::getInstance(). + * It shouldn't matter whether these calls appear in A::A() or + * A::initSingleton(), likewise B::B() or B::initSingleton(). + * + * We promise that if you later call LLSingletonBase::cleanupAll(): + * 1. A::cleanupSingleton() will be called before + * 2. B::cleanupSingleton(), which will be called before + * 3. C::cleanupSingleton(). + * Put differently, if your LLSingleton subclass constructor or + * initSingleton() method explicitly depends on some other LLSingleton + * subclass, you may continue to rely on that other subclass in your + * cleanupSingleton() method. + * + * We introduce a special cleanupSingleton() method because cleanupSingleton() + * operations can involve nontrivial realtime, or might throw an exception. A + * destructor should do neither! + * + * If your cleanupSingleton() method throws an exception, we log that + * exception but proceed with the remaining cleanupSingleton() calls. + * + * Similarly, if at some point you call LLSingletonBase::deleteAll(), all + * remaining LLSingleton instances will be destroyed in dependency order. (Or + * call MySubclass::deleteSingleton() to specifically destroy the canonical + * MySubclass instance.) + * + * As currently written, LLSingleton is not thread-safe. + */  template <typename DERIVED_TYPE> -class LLSingleton : private boost::noncopyable +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; -		} - -		~SingletonLifetimeManager() -		{ -			if (sData.mInitState != DELETED) -			{ -				deleteSingleton(); -			} -		} -	}; -	 + +    // stores pointer to singleton instance +    struct SingletonLifetimeManager +    { +        SingletonLifetimeManager(): +            mMasterRefcount(LLSingletonBase::get_master_refcount()) +        { +            construct(); +        } + +        static void construct() +        { +            sData.mInitState = CONSTRUCTING; +            sData.mInstance = constructSingleton(); +            sData.mInitState = INITIALIZING; +        } + +        ~SingletonLifetimeManager() +        { +            // The dependencies between LLSingletons, and the arbitrary order +            // of static-object destruction, mean that we DO NOT WANT this +            // destructor to delete this LLSingleton. This destructor will run +            // without regard to any other LLSingleton whose cleanup might +            // depend on its existence. What we really want is to count the +            // runtime's attempts to cleanup LLSingleton static data -- and on +            // the very last one, call LLSingletonBase::deleteAll(). That +            // method will properly honor cross-LLSingleton dependencies. This +            // is why we store an intrusive_ptr to a MasterRefcount: our +            // ref_ptr_t member counts SingletonLifetimeManager instances. +            // Once the runtime destroys the last of these, THEN we can delete +            // every remaining LLSingleton. +        } + +        LLSingletonBase::ref_ptr_t mMasterRefcount; +    }; + +protected: +    LLSingleton() +    { +        // populate base-class function pointer with the static +        // deleteSingleton() function for this particular specialization +        mDeleteSingleton = &deleteSingleton; + +        // add this new instance to the master list +        LLSingleton_manage_master<DERIVED_TYPE>().add(this); +    } +  public: -	virtual ~LLSingleton() -	{ -		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; -	} - - -	static DERIVED_TYPE* getInstance() -	{ -		static SingletonLifetimeManager sLifeTimeMgr; - -		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; -		} - -		return NULL; -	} - -	static DERIVED_TYPE* getIfExists() -	{ -		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 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; -	} +    virtual ~LLSingleton() +    { +        // remove this instance from the master list +        LLSingleton_manage_master<DERIVED_TYPE>().remove(this); +        sData.mInstance = NULL; +        sData.mInitState = DELETED; +    } -private: +    /** +     * @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; + +        switch (sData.mInitState) +        { +        case UNINITIALIZED: +            // should never be uninitialized at this point +            logerrs("Uninitialized singleton ", typeid(DERIVED_TYPE).name()); +            return NULL; + +        case CONSTRUCTING: +            logerrs("Tried to access singleton ", typeid(DERIVED_TYPE).name(), +                    " from singleton constructor!"); +            return NULL; -	virtual void initSingleton() {} +        case INITIALIZING: +            // go ahead and flag ourselves as initialized so we can be +            // reentrant during initialization +            sData.mInitState = INITIALIZED;  +            // initialize singleton after constructing it so that it can +            // reference other singletons which in turn depend on it, thus +            // breaking cyclic dependencies +            sData.mInstance->initSingleton(); +            // pop this off stack of initializing singletons +            sData.mInstance->pop_initializing(); +            break; + +        case INITIALIZED: +            break; + +        case DELETED: +            logwarns("Trying to access deleted singleton ", typeid(DERIVED_TYPE).name(), +                     " -- creating new instance"); +            SingletonLifetimeManager::construct(); +            // same as first time construction +            sData.mInitState = INITIALIZED;  +            sData.mInstance->initSingleton();  +            // pop this off stack of initializing singletons +            sData.mInstance->pop_initializing(); +            break; +        } + +        // By this point, if DERIVED_TYPE was pushed onto the initializing +        // stack, it has been popped off. So the top of that stack, if any, is +        // an LLSingleton that directly depends on DERIVED_TYPE. If this call +        // came from another LLSingleton, rather than from vanilla application +        // code, record the dependency. +        sData.mInstance->capture_dependency(sData.mInitState); +        return sData.mInstance; +    } -	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; +    // 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: +    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> diff --git a/indra/llcommon/tests/llpounceable_test.cpp b/indra/llcommon/tests/llpounceable_test.cpp new file mode 100644 index 0000000000..2f4915ce11 --- /dev/null +++ b/indra/llcommon/tests/llpounceable_test.cpp @@ -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 diff --git a/indra/llcommon/tests/llsingleton_test.cpp b/indra/llcommon/tests/llsingleton_test.cpp index 385289aefe..bed436283a 100755 --- a/indra/llcommon/tests/llsingleton_test.cpp +++ b/indra/llcommon/tests/llsingleton_test.cpp @@ -65,7 +65,6 @@ namespace tut  		//Delete the instance  		LLSingletonTest::deleteSingleton(); -		ensure(LLSingletonTest::destroyed());  		ensure(!LLSingletonTest::instanceExists());  		//Construct it again. diff --git a/indra/llcorehttp/tests/llcorehttp_test.cpp b/indra/llcorehttp/tests/llcorehttp_test.cpp index e863ddd13f..19a20e663c 100755 --- a/indra/llcorehttp/tests/llcorehttp_test.cpp +++ b/indra/llcorehttp/tests/llcorehttp_test.cpp @@ -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) diff --git a/indra/llcrashlogger/llcrashlogger.cpp b/indra/llcrashlogger/llcrashlogger.cpp index 7a97c16ea7..0d239c9435 100755 --- a/indra/llcrashlogger/llcrashlogger.cpp +++ b/indra/llcrashlogger/llcrashlogger.cpp @@ -45,6 +45,7 @@  #include "llhttpclient.h"  #include "llsdserialize.h"  #include "llproxy.h" +#include "llcleanup.h"  LLPumpIO* gServicePump = NULL;  BOOL gBreak = false; @@ -587,5 +588,5 @@ bool LLCrashLogger::init()  void LLCrashLogger::commonCleanup()  {  	LLError::logToFile("");   //close crashreport.log -	LLProxy::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLProxy);  } diff --git a/indra/llinventory/lleconomy.h b/indra/llinventory/lleconomy.h index 47fcf688a2..83836b7ce1 100755 --- a/indra/llinventory/lleconomy.h +++ b/indra/llinventory/lleconomy.h @@ -42,19 +42,12 @@ public:  	virtual void onEconomyDataChange() = 0;  }; -class LLGlobalEconomy +class LLGlobalEconomy: public LLSingleton<LLGlobalEconomy>  {  public:  	LLGlobalEconomy();  	virtual ~LLGlobalEconomy(); -	// This class defines its singleton internally as a typedef instead of inheriting from -	// LLSingleton like most others because the LLRegionEconomy sub-class might also -	// become a singleton and this pattern will more easily disambiguate them. -	typedef LLSingleton<LLGlobalEconomy> Singleton; - -	void initSingleton() { } -  	virtual void print();  	void	addObserver(LLEconomyObserver* observer); diff --git a/indra/llmessage/llhttpclientadapter.h b/indra/llmessage/llhttpclientadapter.h index 270282c66f..ab0d219155 100755 --- a/indra/llmessage/llhttpclientadapter.h +++ b/indra/llmessage/llhttpclientadapter.h @@ -28,9 +28,8 @@  #define LL_HTTPCLIENTADAPTER_H  #include "llhttpclientinterface.h" -#include "llsingleton.h"	// LLSingleton<> -class LLHTTPClientAdapter : public LLHTTPClientInterface, public LLSingleton<LLHTTPClientAdapter> +class LLHTTPClientAdapter : public LLHTTPClientInterface  {  public:  	virtual ~LLHTTPClientAdapter(); diff --git a/indra/llmessage/message.cpp b/indra/llmessage/message.cpp index e9ce94ab3b..3c3683f12a 100755 --- a/indra/llmessage/message.cpp +++ b/indra/llmessage/message.cpp @@ -77,6 +77,7 @@  #include "v3math.h"  #include "v4math.h"  #include "lltransfertargetvfile.h" +#include "llpounceable.h"  // Constants  //const char* MESSAGE_LOG_FILENAME = "message.log"; @@ -1776,7 +1777,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*/) @@ -2693,7 +2696,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;  	}  } diff --git a/indra/llmessage/message.h b/indra/llmessage/message.h index 348b09b992..a6fabf2126 100755 --- a/indra/llmessage/message.h +++ b/indra/llmessage/message.h @@ -60,6 +60,7 @@  #include "llmessagesenderinterface.h"  #include "llstoredmessage.h" +#include "llpounceable.h"  const U32 MESSAGE_MAX_STRINGS_LENGTH = 64;  const U32 MESSAGE_NUMBER_OF_HASH_BUCKETS = 8192; @@ -830,7 +831,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. diff --git a/indra/llmessage/tests/llhttpclient_test.cpp b/indra/llmessage/tests/llhttpclient_test.cpp index a32bfa59ce..9356a14f1f 100755 --- a/indra/llmessage/tests/llhttpclient_test.cpp +++ b/indra/llmessage/tests/llhttpclient_test.cpp @@ -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);  		} diff --git a/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp b/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp index 3b04530c1a..e20f61b73f 100755 --- a/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp +++ b/indra/llmessage/tests/lltemplatemessagedispatcher_test.cpp @@ -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; diff --git a/indra/llmessage/tests/lltrustedmessageservice_test.cpp b/indra/llmessage/tests/lltrustedmessageservice_test.cpp index 55748ad27e..41f982a7e2 100755 --- a/indra/llmessage/tests/lltrustedmessageservice_test.cpp +++ b/indra/llmessage/tests/lltrustedmessageservice_test.cpp @@ -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) diff --git a/indra/llui/llfolderview.cpp b/indra/llui/llfolderview.cpp index f60129e601..4c05d001a0 100755 --- a/indra/llui/llfolderview.cpp +++ b/indra/llui/llfolderview.cpp @@ -170,7 +170,8 @@ LLFolderView::LLFolderView(const Params& p)  	mDraggingOverItem(NULL),  	mStatusTextBox(NULL),  	mShowItemLinkOverlays(p.show_item_link_overlays), -	mViewModel(p.view_model) +	mViewModel(p.view_model), +    mGroupedItemModel(p.grouped_item_model)  {  	claimMem(mViewModel);      LLPanel* panel = p.parent_panel; @@ -1810,7 +1811,6 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu)  	}  	// Successively filter out invalid options -  	U32 multi_select_flag = (mSelectedItems.size() > 1 ? ITEM_IN_MULTI_SELECTION : 0x0);  	U32 flags = multi_select_flag | FIRST_SELECTED_ITEM;  	for (selected_items_t::iterator item_itor = mSelectedItems.begin(); @@ -1822,6 +1822,14 @@ void LLFolderView::updateMenuOptions(LLMenuGL* menu)  		flags = multi_select_flag;  	} +	// This adds a check for restrictions based on the entire +	// selection set - for example, any one wearable may not push you +	// over the limit, but all wearables together still might. +    if (getFolderViewGroupedItemModel()) +    { +        getFolderViewGroupedItemModel()->groupFilterContextMenu(mSelectedItems,*menu); +    } +  	addNoOptions(menu);  } diff --git a/indra/llui/llfolderview.h b/indra/llui/llfolderview.h index 08e0a6220a..114dd7bd2f 100755 --- a/indra/llui/llfolderview.h +++ b/indra/llui/llfolderview.h @@ -45,6 +45,7 @@  #include "llscrollcontainer.h"  class LLFolderViewModelInterface; +class LLFolderViewGroupedItemModel;  class LLFolderViewFolder;  class LLFolderViewItem;  class LLFolderViewFilter; @@ -93,6 +94,7 @@ public:  								use_ellipses,  								show_item_link_overlays;  		Mandatory<LLFolderViewModelInterface*>	view_model; +		Optional<LLFolderViewGroupedItemModel*> grouped_item_model;          Mandatory<std::string>   options_menu; @@ -100,7 +102,7 @@ public:  	};  	friend class LLFolderViewScrollContainer; -    typedef std::deque<LLFolderViewItem*> selected_items_t; +    typedef folder_view_item_deque selected_items_t;  	LLFolderView(const Params&);  	virtual ~LLFolderView( void ); @@ -113,6 +115,9 @@ public:  	LLFolderViewModelInterface* getFolderViewModel() { return mViewModel; }  	const LLFolderViewModelInterface* getFolderViewModel() const { return mViewModel; } +    LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() { return mGroupedItemModel; } +    const LLFolderViewGroupedItemModel* getFolderViewGroupedItemModel() const { return mGroupedItemModel; } +      	typedef boost::signals2::signal<void (const std::deque<LLFolderViewItem*>& items, BOOL user_action)> signal_t;  	void setSelectCallback(const signal_t::slot_type& cb) { mSelectSignal.connect(cb); }  	void setReshapeCallback(const signal_t::slot_type& cb) { mReshapeSignal.connect(cb); } @@ -300,6 +305,7 @@ protected:  	LLHandle<LLPanel>               mParentPanel;  	LLFolderViewModelInterface*		mViewModel; +    LLFolderViewGroupedItemModel*   mGroupedItemModel;  	/**  	 * Is used to determine if we need to cut text In LLFolderViewItem to avoid horizontal scroll. diff --git a/indra/llui/llfolderviewitem.h b/indra/llui/llfolderviewitem.h index 0cd20a0f2d..5ad5731cad 100644..100755 --- a/indra/llui/llfolderviewitem.h +++ b/indra/llui/llfolderviewitem.h @@ -454,5 +454,12 @@ public:  	template<typename SORT_FUNC> void sortItems(const SORT_FUNC& func) { mItems.sort(func); }  }; +typedef std::deque<LLFolderViewItem*> folder_view_item_deque; + +class LLFolderViewGroupedItemModel: public LLRefCount +{ +public: +    virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu) = 0; +};  #endif  // LLFOLDERVIEWITEM_H diff --git a/indra/llui/llspellcheck.h b/indra/llui/llspellcheck.h index 4ab80195ea..5ecc9aa110 100755 --- a/indra/llui/llspellcheck.h +++ b/indra/llui/llspellcheck.h @@ -29,6 +29,7 @@  #include "llsingleton.h"  #include "llui.h" +#include "llinitdestroyclass.h"  #include <boost/signals2.hpp>  class Hunspell; diff --git a/indra/llui/llui.cpp b/indra/llui/llui.cpp index aabc7ed2e4..cc186f4997 100755 --- a/indra/llui/llui.cpp +++ b/indra/llui/llui.cpp @@ -60,6 +60,7 @@  #include "llflyoutbutton.h"  #include "llsearcheditor.h"  #include "lltoolbar.h" +#include "llcleanup.h"  // for XUIParse  #include "llquaternion.h" @@ -208,7 +209,7 @@ void LLUI::initClass(const settings_map_t& settings,  void LLUI::cleanupClass()  { -	LLRender2D::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLRender2D);  }  void LLUI::setPopupFuncs(const add_popup_t& add_popup, const remove_popup_t& remove_popup,  const clear_popups_t& clear_popups) diff --git a/indra/llui/llui.h b/indra/llui/llui.h index c727f75c4f..d7151dbee9 100755 --- a/indra/llui/llui.h +++ b/indra/llui/llui.h @@ -344,95 +344,6 @@ private:  // Moved LLLocalClipRect to lllocalcliprect.h -class LLCallbackRegistry -{ -public: -	typedef boost::signals2::signal<void()> callback_signal_t; -	 -	void registerCallback(const callback_signal_t::slot_type& slot) -	{ -		mCallbacks.connect(slot); -	} - -	void fireCallbacks() -	{ -		mCallbacks(); -	} - -private: -	callback_signal_t mCallbacks; -}; - -class LLInitClassList :  -	public LLCallbackRegistry,  -	public LLSingleton<LLInitClassList> -{ -	friend class LLSingleton<LLInitClassList>; -private: -	LLInitClassList() {} -}; - -class LLDestroyClassList :  -	public LLCallbackRegistry,  -	public LLSingleton<LLDestroyClassList> -{ -	friend class LLSingleton<LLDestroyClassList>; -private: -	LLDestroyClassList() {} -}; - -template<typename T> -class LLRegisterWith -{ -public: -	LLRegisterWith(boost::function<void ()> func) -	{ -		T::instance().registerCallback(func); -	} - -	// this avoids a MSVC bug where non-referenced static members are "optimized" away -	// even if their constructors have side effects -	S32 reference() -	{ -		S32 dummy; -		dummy = 0; -		return dummy; -	} -}; - -template<typename T> -class LLInitClass -{ -public: -	LLInitClass() { sRegister.reference(); } - -	static LLRegisterWith<LLInitClassList> sRegister; -private: - -	static void initClass() -	{ -		LL_ERRS() << "No static initClass() method defined for " << typeid(T).name() << LL_ENDL; -	} -}; - -template<typename T> -class LLDestroyClass -{ -public: -	LLDestroyClass() { sRegister.reference(); } - -	static LLRegisterWith<LLDestroyClassList> sRegister; -private: - -	static void destroyClass() -	{ -		LL_ERRS() << "No static destroyClass() method defined for " << typeid(T).name() << LL_ENDL; -	} -}; - -template <typename T> LLRegisterWith<LLInitClassList> LLInitClass<T>::sRegister(&T::initClass); -template <typename T> LLRegisterWith<LLDestroyClassList> LLDestroyClass<T>::sRegister(&T::destroyClass); -  // useful parameter blocks  struct TimeIntervalParam : public LLInitParam::ChoiceBlock<TimeIntervalParam>  { diff --git a/indra/llui/tests/llurlentry_stub.cpp b/indra/llui/tests/llurlentry_stub.cpp index 5d3f9ac327..d28f601009 100755 --- a/indra/llui/tests/llurlentry_stub.cpp +++ b/indra/llui/tests/llurlentry_stub.cpp @@ -31,6 +31,7 @@  #include "llcachename.h"  #include "lluuid.h"  #include "message.h" +#include "llpounceable.h"  #include <string> @@ -167,7 +168,7 @@ char const* const _PREHASH_AgentID = (char *)"AgentID";  LLHost LLHost::invalid(INVALID_PORT,INVALID_HOST_IP_ADDRESS); -LLMessageSystem* gMessageSystem = NULL; +LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;  //  // Stub implementation for LLMessageSystem diff --git a/indra/lscript/lscript_compile/indra.l b/indra/lscript/lscript_compile/indra.l index 7772c95609..d5c71d9987 100755 --- a/indra/lscript/lscript_compile/indra.l +++ b/indra/lscript/lscript_compile/indra.l @@ -102,7 +102,7 @@ int yyerror(const char *fmt, ...);  "event"				{ count(); return(EVENT); }  "jump"				{ count(); return(JUMP); }  "return"			{ count(); return(RETURN); } -"if"				{ count(); return(IF); } +"if"				{ count(); return(IFF); }  "else"				{ count(); return(ELSE); }  "for"				{ count(); return(FOR); }  "do"				{ count(); return(DO); } diff --git a/indra/lscript/lscript_compile/indra.y b/indra/lscript/lscript_compile/indra.y index a0a034d21c..604b5b6b92 100755 --- a/indra/lscript/lscript_compile/indra.y +++ b/indra/lscript/lscript_compile/indra.y @@ -117,7 +117,7 @@  %token					SHIFT_LEFT  %token					SHIFT_RIGHT -%token					IF +%token					IFF         /* IF used by a helper library */  %token					ELSE  %token					FOR  %token					DO @@ -1312,13 +1312,13 @@ statement  	{   		$$ = $1;  	} -	| IF '(' expression ')' statement	%prec LOWER_THAN_ELSE			 +	| IFF '(' expression ')' statement	%prec LOWER_THAN_ELSE			  	{    		$$ = new LLScriptIf(gLine, gColumn, $3, $5);  		$5->mAllowDeclarations = FALSE;  		gAllocationManager->addAllocation($$);  	} -	| IF '(' expression ')' statement ELSE statement					 +	| IFF '(' expression ')' statement ELSE statement					  	{    		$$ = new LLScriptIfElse(gLine, gColumn, $3, $5, $7);  		$5->mAllowDeclarations = FALSE; diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt index 1523336b0d..ef239df8cd 100644 --- a/indra/newview/VIEWER_VERSION.txt +++ b/indra/newview/VIEWER_VERSION.txt @@ -1 +1 @@ -3.7.29 +3.7.30 diff --git a/indra/newview/llagentwearables.cpp b/indra/newview/llagentwearables.cpp index f06ffb4fb3..5589ab4aa7 100755 --- a/indra/newview/llagentwearables.cpp +++ b/indra/newview/llagentwearables.cpp @@ -633,10 +633,13 @@ void LLAgentWearables::wearableUpdated(LLWearable *wearable, BOOL removed)  		// the versions themselves are compatible. This code can be removed before release.  		if( wearable->getDefinitionVersion() == 24 )  		{ -			wearable->setDefinitionVersion(22); -			U32 index = getWearableIndex(wearable); -			LL_INFOS() << "forcing wearable type " << wearable->getType() << " to version 22 from 24" << LL_ENDL; -			saveWearable(wearable->getType(),index); +			U32 index; +			if (getWearableIndex(wearable,index)) +			{ +				LL_INFOS() << "forcing wearable type " << wearable->getType() << " to version 22 from 24" << LL_ENDL; +				wearable->setDefinitionVersion(22); +				saveWearable(wearable->getType(),index); +			}  		}  		checkWearableAgainstInventory(viewer_wearable); @@ -949,7 +952,7 @@ void LLAgentWearables::removeWearableFinal(const LLWearableType::EType type, boo  			LLViewerWearable* old_wearable = getViewerWearable(type,i);  			if (old_wearable)  			{ -				popWearable(old_wearable); +				eraseWearable(old_wearable);  				old_wearable->removeFromAvatar();  			}  		} @@ -961,7 +964,7 @@ void LLAgentWearables::removeWearableFinal(const LLWearableType::EType type, boo  		if (old_wearable)  		{ -			popWearable(old_wearable); +			eraseWearable(old_wearable);  			old_wearable->removeFromAvatar();  		}  	} @@ -1163,7 +1166,13 @@ bool LLAgentWearables::onSetWearableDialog(const LLSD& notification, const LLSD&  {  	S32 option = LLNotificationsUtil::getSelectedOption(notification, response);  	LLInventoryItem* new_item = gInventory.getItem(notification["payload"]["item_id"].asUUID()); -	U32 index = gAgentWearables.getWearableIndex(wearable); +	U32 index; +	if (!gAgentWearables.getWearableIndex(wearable,index)) +	{ +		LL_WARNS() << "Wearable not found" << LL_ENDL; +		delete wearable; +		return false; +	}  	if (!new_item)  	{  		delete wearable; diff --git a/indra/newview/llagentwearables.h b/indra/newview/llagentwearables.h index 1004482020..7dc23c6402 100755 --- a/indra/newview/llagentwearables.h +++ b/indra/newview/llagentwearables.h @@ -38,6 +38,7 @@  #include "llviewerinventory.h"  #include "llavatarappearancedefines.h"  #include "llwearabledata.h" +#include "llinitdestroyclass.h"  class LLInventoryItem;  class LLVOAvatarSelf; diff --git a/indra/newview/llappearancemgr.cpp b/indra/newview/llappearancemgr.cpp index a64d5b50b3..b3317e937e 100755 --- a/indra/newview/llappearancemgr.cpp +++ b/indra/newview/llappearancemgr.cpp @@ -1338,90 +1338,113 @@ void wear_on_avatar_cb(const LLUUID& inv_item, bool do_replace = false)  	}  } -bool LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear, +void LLAppearanceMgr::wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, +                                        bool do_update, +                                        bool replace, +                                        LLPointer<LLInventoryCallback> cb) +{ +    bool first = true; + +    LLInventoryObject::const_object_list_t items_to_link; + +    for (uuid_vec_t::const_iterator it = item_ids_to_wear.begin(); +         it != item_ids_to_wear.end(); +         ++it) +    { +        replace = first && replace; +        first = false; + +        const LLUUID& item_id_to_wear = *it; + +        if (item_id_to_wear.isNull()) continue; + +        LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear); +        if (!item_to_wear) continue; + +        if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID())) +        { +            LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace)); +            copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(), cb); +            continue; +        }  +        else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID())) +        { +            continue; // not in library and not in agent's inventory +        } +        else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH))) +        { +            LLNotificationsUtil::add("CannotWearTrash"); +            continue; +        } +        else if (isLinkedInCOF(item_to_wear->getUUID())) // EXT-84911 +        { +            continue; +        } + +        switch (item_to_wear->getType()) +        { +            case LLAssetType::AT_CLOTHING: +            { +                if (gAgentWearables.areWearablesLoaded()) +                { +                    if (!cb && do_update) +                    { +                        cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear); +                    } +                    LLWearableType::EType type = item_to_wear->getWearableType(); +                    S32 wearable_count = gAgentWearables.getWearableCount(type); +                    if ((replace && wearable_count != 0) || !gAgentWearables.canAddWearable(type)) +                    { +                        LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), +                                                                           wearable_count-1); +                        removeCOFItemLinks(item_id, cb); +                    } +                     +                    items_to_link.push_back(item_to_wear); +                }  +            } +            break; + +            case LLAssetType::AT_BODYPART: +            { +                // TODO: investigate wearables may not be loaded at this point EXT-8231 +                 +                // Remove the existing wearables of the same type. +                // Remove existing body parts anyway because we must not be able to wear e.g. two skins. +                removeCOFLinksOfType(item_to_wear->getWearableType()); +                if (!cb && do_update) +                { +                    cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear); +                } +                items_to_link.push_back(item_to_wear); +            } +            break; +                 +            case LLAssetType::AT_OBJECT: +            { +                rez_attachment(item_to_wear, NULL, replace); +            } +            break; + +            default: continue; +        } +    } + +    // Batch up COF link creation - more efficient if using AIS. +    if (items_to_link.size()) +    { +        link_inventory_array(getCOF(), items_to_link, cb);  +    } +} + +void LLAppearanceMgr::wearItemOnAvatar(const LLUUID& item_id_to_wear,  									   bool do_update,  									   bool replace,  									   LLPointer<LLInventoryCallback> cb)  { - -	if (item_id_to_wear.isNull()) return false; - -	// *TODO: issue with multi-wearable should be fixed: -	// in this case this method will be called N times - loading started for each item -	// and than N times will be called - loading completed for each item. -	// That means subscribers will be notified that loading is done after first item in a batch is worn. -	// (loading indicator disappears for example before all selected items are worn) -	// Have not fix this issue for 2.1 because of stability reason. EXT-7777. - -	// Disabled for now because it is *not* acceptable to call updateAppearanceFromCOF() multiple times -//	gAgentWearables.notifyLoadingStarted(); - -	LLViewerInventoryItem* item_to_wear = gInventory.getItem(item_id_to_wear); -	if (!item_to_wear) return false; - -	if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getLibraryRootFolderID())) -	{ -		LLPointer<LLInventoryCallback> cb = new LLBoostFuncInventoryCallback(boost::bind(wear_on_avatar_cb,_1,replace)); -		copy_inventory_item(gAgent.getID(), item_to_wear->getPermissions().getOwner(), item_to_wear->getUUID(), LLUUID::null, std::string(), cb); -		return false; -	}  -	else if (!gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.getRootFolderID())) -	{ -		return false; // not in library and not in agent's inventory -	} -	else if (gInventory.isObjectDescendentOf(item_to_wear->getUUID(), gInventory.findCategoryUUIDForType(LLFolderType::FT_TRASH))) -	{ -		LLNotificationsUtil::add("CannotWearTrash"); -		return false; -	} -	else if (isLinkedInCOF(item_to_wear->getUUID())) // EXT-84911 -	{ -		return false; -	} - -	switch (item_to_wear->getType()) -	{ -		case LLAssetType::AT_CLOTHING: -		if (gAgentWearables.areWearablesLoaded()) -		{ -			if (!cb && do_update) -			{ -				cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear); -			} -			S32 wearable_count = gAgentWearables.getWearableCount(item_to_wear->getWearableType()); -			if ((replace && wearable_count != 0) || -				(wearable_count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) ) -			{ -				LLUUID item_id = gAgentWearables.getWearableItemID(item_to_wear->getWearableType(), -																   wearable_count-1); -				removeCOFItemLinks(item_id, cb); -			} - -			addCOFItemLink(item_to_wear, cb); -		}  -		break; - -		case LLAssetType::AT_BODYPART: -		// TODO: investigate wearables may not be loaded at this point EXT-8231 -		 -		// Remove the existing wearables of the same type. -		// Remove existing body parts anyway because we must not be able to wear e.g. two skins. -		removeCOFLinksOfType(item_to_wear->getWearableType()); -		if (!cb && do_update) -		{ -			cb = new LLUpdateAppearanceAndEditWearableOnDestroy(item_id_to_wear); -		} -		addCOFItemLink(item_to_wear, cb); -		break; - -		case LLAssetType::AT_OBJECT: -		rez_attachment(item_to_wear, NULL, replace); -		break; - -		default: return false;; -	} - -	return true; +    uuid_vec_t ids; +    ids.push_back(item_id_to_wear); +    wearItemsOnAvatar(ids, do_update, replace, cb);  }  // Update appearance from outfit folder. @@ -1782,6 +1805,49 @@ bool LLAppearanceMgr::getCanReplaceCOF(const LLUUID& outfit_cat_id)  	return items.size() > 0;  } +// Moved from LLWearableList::ContextMenu for wider utility. +bool LLAppearanceMgr::canAddWearables(const uuid_vec_t& item_ids) +{ +	// TODO: investigate wearables may not be loaded at this point EXT-8231 + +	U32 n_objects = 0; +	U32 n_clothes = 0; + +	// Count given clothes (by wearable type) and objects. +	for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it) +	{ +		LLViewerInventoryItem* item = gInventory.getItem(*it); +		if (!item) +		{ +			return false; +		} + +		if (item->getType() == LLAssetType::AT_OBJECT) +		{ +			++n_objects; +		} +		else if (item->getType() == LLAssetType::AT_CLOTHING) +		{ +			++n_clothes; +		} +		else +		{ +			LL_WARNS() << "Unexpected wearable type" << LL_ENDL; +			return false; +		} +	} + +	// Check whether we can add all the objects. +	if (!isAgentAvatarValid() || !gAgentAvatarp->canAttachMoreObjects(n_objects)) +	{ +		return false; +	} + +	// Check whether we can add all the clothes. +    U32 sum_clothes = n_clothes + gAgentWearables.getClothingLayerCount(); +    return sum_clothes <= LLAgentWearables::MAX_CLOTHING_LAYERS; +} +  void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category, LLPointer<LLInventoryCallback> cb)  {  	LLInventoryModel::cat_array_t cats; @@ -1804,25 +1870,39 @@ void LLAppearanceMgr::purgeBaseOutfitLink(const LLUUID& category, LLPointer<LLIn  // Keep the last N wearables of each type.  For viewer 2.0, N is 1 for  // both body parts and clothing items.  void LLAppearanceMgr::filterWearableItems( -	LLInventoryModel::item_array_t& items, S32 max_per_type) -{ -	// Divvy items into arrays by wearable type. -	std::vector<LLInventoryModel::item_array_t> items_by_type(LLWearableType::WT_COUNT); -	divvyWearablesByType(items, items_by_type); - -	// rebuild items list, retaining the last max_per_type of each array -	items.clear(); -	for (S32 i=0; i<LLWearableType::WT_COUNT; i++) -	{ -		S32 size = items_by_type[i].size(); -		if (size <= 0) -			continue; -		S32 start_index = llmax(0,size-max_per_type); -		for (S32 j = start_index; j<size; j++) -		{ -			items.push_back(items_by_type[i][j]); -		} -	} +	LLInventoryModel::item_array_t& items, S32 max_per_type, S32 max_total) +{ +    // Restrict by max total items first. +    if ((max_total > 0) && (items.size() > max_total)) +    { +        LLInventoryModel::item_array_t items_to_keep; +        for (S32 i=0; i<max_total; i++) +        { +            items_to_keep.push_back(items[i]); +        } +        items = items_to_keep; +    } + +    if (max_per_type > 0) +    { +        // Divvy items into arrays by wearable type. +        std::vector<LLInventoryModel::item_array_t> items_by_type(LLWearableType::WT_COUNT); +        divvyWearablesByType(items, items_by_type); + +        // rebuild items list, retaining the last max_per_type of each array +        items.clear(); +        for (S32 i=0; i<LLWearableType::WT_COUNT; i++) +        { +            S32 size = items_by_type[i].size(); +            if (size <= 0) +                continue; +            S32 start_index = llmax(0,size-max_per_type); +            for (S32 j = start_index; j<size; j++) +            { +                items.push_back(items_by_type[i][j]); +            } +        } +    }  }  void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append) @@ -1864,7 +1944,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)  		reverse(body_items.begin(), body_items.end());  	// Reduce body items to max of one per type.  	removeDuplicateItems(body_items); -	filterWearableItems(body_items, 1); +	filterWearableItems(body_items, 1, 0);  	// - Wearables: include COF contents only if appending.  	LLInventoryModel::item_array_t wear_items; @@ -1873,7 +1953,7 @@ void LLAppearanceMgr::updateCOF(const LLUUID& category, bool append)  	getDescendentsOfAssetType(category, wear_items, LLAssetType::AT_CLOTHING);  	// Reduce wearables to max of one per type.  	removeDuplicateItems(wear_items); -	filterWearableItems(wear_items, LLAgentWearables::MAX_CLOTHING_PER_TYPE); +	filterWearableItems(wear_items, 0, LLAgentWearables::MAX_CLOTHING_LAYERS);  	// - Attachments: include COF contents only if appending.  	LLInventoryModel::item_array_t obj_items; @@ -2062,7 +2142,8 @@ void item_array_diff(LLInventoryModel::item_array_t& full_list,  S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,  												 LLAssetType::EType type, -												 S32 max_items, +												 S32 max_items_per_type, +												 S32 max_items_total,  												 LLInventoryObject::object_list_t& items_to_kill)  {  	S32 to_kill_count = 0; @@ -2071,9 +2152,9 @@ S32 LLAppearanceMgr::findExcessOrDuplicateItems(const LLUUID& cat_id,  	getDescendentsOfAssetType(cat_id, items, type);  	LLInventoryModel::item_array_t curr_items = items;  	removeDuplicateItems(items); -	if (max_items > 0) +	if (max_items_per_type > 0 || max_items_total > 0)  	{ -		filterWearableItems(items, max_items); +		filterWearableItems(items, max_items_per_type, max_items_total);  	}  	LLInventoryModel::item_array_t kill_items;  	item_array_diff(curr_items,items,kill_items); @@ -2092,11 +2173,11 @@ void LLAppearanceMgr::findAllExcessOrDuplicateItems(const LLUUID& cat_id,  													LLInventoryObject::object_list_t& items_to_kill)  {  	findExcessOrDuplicateItems(cat_id,LLAssetType::AT_BODYPART, -							   1, items_to_kill); +							   1, 0, items_to_kill);  	findExcessOrDuplicateItems(cat_id,LLAssetType::AT_CLOTHING, -							   LLAgentWearables::MAX_CLOTHING_PER_TYPE, items_to_kill); +							   0, LLAgentWearables::MAX_CLOTHING_LAYERS, items_to_kill);  	findExcessOrDuplicateItems(cat_id,LLAssetType::AT_OBJECT, -							   -1, items_to_kill); +							   0, 0, items_to_kill);  }  void LLAppearanceMgr::enforceCOFItemRestrictions(LLPointer<LLInventoryCallback> cb) @@ -2588,7 +2669,6 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item,  								  item_array,  								  LLInventoryModel::EXCLUDE_TRASH);  	bool linked_already = false; -	U32 count = 0;  	for (S32 i=0; i<item_array.size(); i++)  	{  		// Are these links to the same object? @@ -2608,14 +2688,13 @@ void LLAppearanceMgr::addCOFItemLink(const LLInventoryItem *item,  		// type? If so, new item will replace old.  		else if ((vitem->isWearableType()) && (vitem->getWearableType() == wearable_type))  		{ -			++count; -			if (is_body_part && inv_item->getIsLinkType()  && (vitem->getWearableType() == wearable_type)) +			if (is_body_part && inv_item->getIsLinkType())  			{  				remove_inventory_item(inv_item->getUUID(), cb);  			} -			else if (count >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) +			else if (!gAgentWearables.canAddWearable(wearable_type))  			{ -				// MULTI-WEARABLES: make sure we don't go over MAX_CLOTHING_PER_TYPE +				// MULTI-WEARABLES: make sure we don't go over clothing limits  				remove_inventory_item(inv_item->getUUID(), cb);  			}  		} @@ -4071,16 +4150,7 @@ void callAfterCategoryFetch(const LLUUID& cat_id, nullary_func_t cb)  void wear_multiple(const uuid_vec_t& ids, bool replace)  {  	LLPointer<LLInventoryCallback> cb = new LLUpdateAppearanceOnDestroy; -	 -	bool first = true; -	uuid_vec_t::const_iterator it; -	for (it = ids.begin(); it != ids.end(); ++it) -	{ -		// if replace is requested, the first item worn will replace the current top -		// item, and others will be added. -		LLAppearanceMgr::instance().wearItemOnAvatar(*it,false,first && replace,cb); -		first = false; -	} +    LLAppearanceMgr::instance().wearItemsOnAvatar(ids, false, replace, cb);  }  // SLapp for easy-wearing of a stock (library) avatar diff --git a/indra/newview/llappearancemgr.h b/indra/newview/llappearancemgr.h index 7742a19c07..ee9d3b7209 100755 --- a/indra/newview/llappearancemgr.h +++ b/indra/newview/llappearancemgr.h @@ -67,7 +67,8 @@ public:  	void addCategoryToCurrentOutfit(const LLUUID& cat_id);  	S32 findExcessOrDuplicateItems(const LLUUID& cat_id,  								   LLAssetType::EType type, -								   S32 max_items, +								   S32 max_items_per_type, +								   S32 max_items_total,  								   LLInventoryObject::object_list_t& items_to_kill);  	void findAllExcessOrDuplicateItems(const LLUUID& cat_id,  									  LLInventoryObject::object_list_t& items_to_kill); @@ -99,6 +100,9 @@ public:  	// Determine whether we can replace current outfit with the given one.  	bool getCanReplaceCOF(const LLUUID& outfit_cat_id); +    // Can we add all referenced items to the avatar? +    bool canAddWearables(const uuid_vec_t& item_ids); +      	// Copy all items in a category.  	void shallowCopyCategoryContents(const LLUUID& src_id, const LLUUID& dst_id,  									 LLPointer<LLInventoryCallback> cb); @@ -117,8 +121,13 @@ public:  	// find the UUID of the currently worn outfit (Base Outfit)  	const LLUUID getBaseOutfitUUID(); +    void wearItemsOnAvatar(const uuid_vec_t& item_ids_to_wear, +                           bool do_update, +                           bool replace, +                           LLPointer<LLInventoryCallback> cb = NULL); +  	// Wear/attach an item (from a user's inventory) on the agent -	bool wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update, bool replace = false, +	void wearItemOnAvatar(const LLUUID& item_to_wear, bool do_update, bool replace = false,  						  LLPointer<LLInventoryCallback> cb = NULL);  	// Update the displayed outfit name in UI. @@ -235,7 +244,7 @@ protected:  private: -	void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type); +	void filterWearableItems(LLInventoryModel::item_array_t& items, S32 max_per_type, S32 max_total);  	void getDescendentsOfAssetType(const LLUUID& category,   										  LLInventoryModel::item_array_t& items, diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 6dc71bc94e..6a64f67f9c 100755 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -226,6 +226,7 @@  #include "llsecapi.h"  #include "llmachineid.h"  #include "llmainlooprepeater.h" +#include "llcleanup.h"  #include "llviewereventrecorder.h" @@ -1764,7 +1765,7 @@ bool LLAppViewer::cleanup()  	gTransferManager.cleanup();  #endif -	LLLocalBitmapMgr::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLLocalBitmapMgr);  	// Note: this is where gWorldMap used to be deleted. @@ -1872,11 +1873,11 @@ bool LLAppViewer::cleanup()  	LLViewerObject::cleanupVOClasses(); -	LLAvatarAppearance::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLAvatarAppearance); -	LLAvatarAppearance::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLAvatarAppearance); -	LLPostProcess::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLPostProcess);  	LLTracker::cleanupInstance(); @@ -1902,12 +1903,12 @@ bool LLAppViewer::cleanup()   	//end_messaging_system(); -	LLFollowCamMgr::cleanupClass(); -	//LLVolumeMgr::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLFollowCamMgr); +	//SUBSYSTEM_CLEANUP(LLVolumeMgr);  	LLPrimitive::cleanupVolumeManager(); -	LLWorldMapView::cleanupClass(); -	LLFolderViewItem::cleanupClass(); -	LLUI::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLWorldMapView); +	SUBSYSTEM_CLEANUP(LLFolderViewItem); +	SUBSYSTEM_CLEANUP(LLUI);  	//  	// Shut down the VFS's AFTER the decode manager cleans up (since it cleans up vfiles). @@ -1916,7 +1917,7 @@ bool LLAppViewer::cleanup()  	//  	LL_INFOS() << "Cleaning up VFS" << LL_ENDL; -	LLVFile::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLVFile);  	LL_INFOS() << "Saving Data" << LL_ENDL; @@ -2020,7 +2021,7 @@ bool LLAppViewer::cleanup()  	// *NOTE:Mani - The following call is not thread safe.   	LL_CHECK_MEMORY -	LLCurl::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLCurl);  	LL_CHECK_MEMORY  	// Non-LLCurl libcurl library @@ -2029,9 +2030,9 @@ bool LLAppViewer::cleanup()  	// NOTE The following call is not thread safe.   	ll_cleanup_ares(); -	LLFilePickerThread::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLFilePickerThread); -	//MUST happen AFTER LLCurl::cleanupClass +	//MUST happen AFTER SUBSYSTEM_CLEANUP(LLCurl)  	delete sTextureCache;      sTextureCache = NULL;  	delete sTextureFetch; @@ -2060,17 +2061,17 @@ bool LLAppViewer::cleanup()  	LL_INFOS() << "Cleaning up Media and Textures" << LL_ENDL;  	//Note: -	//LLViewerMedia::cleanupClass() has to be put before gTextureList.shutdown() +	//SUBSYSTEM_CLEANUP(LLViewerMedia) has to be put before gTextureList.shutdown()  	//because some new image might be generated during cleaning up media. --bao -	LLViewerMedia::cleanupClass(); -	LLViewerParcelMedia::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLViewerMedia); +	SUBSYSTEM_CLEANUP(LLViewerParcelMedia);  	gTextureList.shutdown(); // shutdown again in case a callback added something  	LLUIImageList::getInstance()->cleanUp();  	// This should eventually be done in LLAppViewer -	LLImage::cleanupClass(); -	LLVFSThread::cleanupClass(); -	LLLFSThread::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLImage); +	SUBSYSTEM_CLEANUP(LLVFSThread); +	SUBSYSTEM_CLEANUP(LLLFSThread);  #ifndef LL_RELEASE_FOR_DOWNLOAD  	LL_INFOS() << "Auditing VFS" << LL_ENDL; @@ -2113,9 +2114,9 @@ bool LLAppViewer::cleanup()  		LL_INFOS() << "File launched." << LL_ENDL;  	}  	LL_INFOS() << "Cleaning up LLProxy." << LL_ENDL; -	LLProxy::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLProxy); -	LLWearableType::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLWearableType);  	LLMainLoopRepeater::instance().stop(); diff --git a/indra/newview/llassetuploadresponders.cpp b/indra/newview/llassetuploadresponders.cpp index d2b1dcbf35..620274c1a5 100755 --- a/indra/newview/llassetuploadresponders.cpp +++ b/indra/newview/llassetuploadresponders.cpp @@ -334,7 +334,7 @@ void LLAssetUploadResponder::uploadFailure(const LLSD& content)  	// deal with L$ errors  	if (reason == "insufficient funds")  	{ -		S32 price = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); +		S32 price = LLGlobalEconomy::getInstance()->getPriceUpload();  		LLStringUtil::format_map_t args;  		args["AMOUNT"] = llformat("%d", price);  		LLBuyCurrencyHTML::openCurrencyFloater( LLTrans::getString("uploading_costs", args), price ); @@ -405,7 +405,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content)  		asset_type == LLAssetType::AT_MESH)  	{  		expected_upload_cost =  -			LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); +			LLGlobalEconomy::getInstance()->getPriceUpload();  	}  	on_new_single_inventory_upload_complete( @@ -467,7 +467,7 @@ void LLNewAgentInventoryResponder::uploadComplete(const LLSD& content)  			everyone_perms,  			display_name,  			callback, -			LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(), +			LLGlobalEconomy::getInstance()->getPriceUpload(),  			userdata);  	} diff --git a/indra/newview/llfavoritesbar.h b/indra/newview/llfavoritesbar.h index a370724947..574617fa63 100755 --- a/indra/newview/llfavoritesbar.h +++ b/indra/newview/llfavoritesbar.h @@ -34,6 +34,7 @@  #include "llinventoryobserver.h"  #include "llinventorymodel.h"  #include "llviewerinventory.h" +#include "llinitdestroyclass.h"  class LLMenuItemCallGL;  class LLToggleableMenu; diff --git a/indra/newview/llfloaterbvhpreview.cpp b/indra/newview/llfloaterbvhpreview.cpp index 669ffa7c59..75cbc6561a 100755 --- a/indra/newview/llfloaterbvhpreview.cpp +++ b/indra/newview/llfloaterbvhpreview.cpp @@ -993,7 +993,7 @@ void LLFloaterBvhPreview::onBtnOK(void* userdata)  				std::string name = floaterp->getChild<LLUICtrl>("name_form")->getValue().asString();  				std::string desc = floaterp->getChild<LLUICtrl>("description_form")->getValue().asString();  				LLAssetStorage::LLStoreAssetCallback callback = NULL; -				S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); +				S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();  				void *userdata = NULL;  				upload_new_resource(floaterp->mTransactionID, // tid  						    LLAssetType::AT_ANIMATION, diff --git a/indra/newview/llfloaterimcontainer.cpp b/indra/newview/llfloaterimcontainer.cpp index ab57e8c170..cacd66aee9 100755 --- a/indra/newview/llfloaterimcontainer.cpp +++ b/indra/newview/llfloaterimcontainer.cpp @@ -101,7 +101,7 @@ LLFloaterIMContainer::~LLFloaterIMContainer()  	gSavedPerAccountSettings.setBOOL("ConversationsMessagePaneCollapsed", mMessagesPane->isCollapsed());  	gSavedPerAccountSettings.setBOOL("ConversationsParticipantListCollapsed", !isParticipantListExpanded()); -	if (!LLSingleton<LLIMMgr>::destroyed()) +	if (LLIMMgr::instanceExists())  	{  		LLIMMgr::getInstance()->removeSessionObserver(this);  	} diff --git a/indra/newview/llfloaternamedesc.cpp b/indra/newview/llfloaternamedesc.cpp index 0cca715fe2..a889ca2545 100755 --- a/indra/newview/llfloaternamedesc.cpp +++ b/indra/newview/llfloaternamedesc.cpp @@ -121,7 +121,7 @@ BOOL LLFloaterNameDesc::postBuild()  	// Cancel button  	getChild<LLUICtrl>("cancel_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnCancel, this)); -	getChild<LLUICtrl>("ok_btn")->setLabelArg("[AMOUNT]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload() )); +	getChild<LLUICtrl>("ok_btn")->setLabelArg("[AMOUNT]", llformat("%d", LLGlobalEconomy::getInstance()->getPriceUpload() ));  	setDefaultBtn("ok_btn"); @@ -160,7 +160,7 @@ void LLFloaterNameDesc::onBtnOK( )  	getChildView("ok_btn")->setEnabled(FALSE); // don't allow inadvertent extra uploads  	LLAssetStorage::LLStoreAssetCallback callback = NULL; -	S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); // kinda hack - assumes that unsubclassed LLFloaterNameDesc is only used for uploading chargeable assets, which it is right now (it's only used unsubclassed for the sound upload dialog, and THAT should be a subclass). +	S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload(); // kinda hack - assumes that unsubclassed LLFloaterNameDesc is only used for uploading chargeable assets, which it is right now (it's only used unsubclassed for the sound upload dialog, and THAT should be a subclass).  	void *nruserdata = NULL;  	std::string display_name = LLStringUtil::null; diff --git a/indra/newview/llhints.h b/indra/newview/llhints.h index ebffe561b9..dd6195a9ce 100755 --- a/indra/newview/llhints.h +++ b/indra/newview/llhints.h @@ -29,6 +29,7 @@  #include "llpanel.h"  #include "llnotifications.h" +#include "llinitdestroyclass.h"  class LLHints :  public LLInitClass<LLHints> diff --git a/indra/newview/llimview.h b/indra/newview/llimview.h index f92eff4845..79be6fd9d3 100755 --- a/indra/newview/llimview.h +++ b/indra/newview/llimview.h @@ -33,6 +33,7 @@  #include "lllogchat.h"  #include "llvoicechannel.h" +#include "llinitdestroyclass.h"  class LLAvatarName;  class LLFriendObserver; diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp index 1dc5558555..a047ed6fee 100755 --- a/indra/newview/llinventorybridge.cpp +++ b/indra/newview/llinventorybridge.cpp @@ -74,6 +74,7 @@  #include "llviewerwindow.h"  #include "llvoavatarself.h"  #include "llwearablelist.h" +#include "llwearableitemslist.h"  #include "lllandmarkactions.h"  #include "llpanellandmarks.h" @@ -557,6 +558,46 @@ BOOL LLInvFVBridge::isClipboardPasteableAsLink() const  	return TRUE;  } +void disable_context_entries_if_present(LLMenuGL& menu, +                                        const menuentry_vec_t &disabled_entries) +{ +	const LLView::child_list_t *list = menu.getChildList(); +	for (LLView::child_list_t::const_iterator itor = list->begin();  +		 itor != list->end();  +		 ++itor) +	{ +		LLView *menu_item = (*itor); +		std::string name = menu_item->getName(); + +		// descend into split menus: +		LLMenuItemBranchGL* branchp = dynamic_cast<LLMenuItemBranchGL*>(menu_item); +		if ((name == "More") && branchp) +		{ +			disable_context_entries_if_present(*branchp->getBranch(), disabled_entries); +		} + +		bool found = false; +		menuentry_vec_t::const_iterator itor2; +		for (itor2 = disabled_entries.begin(); itor2 != disabled_entries.end(); ++itor2) +		{ +			if (*itor2 == name) +			{ +				found = true; +				break; +			} +		} + +        if (found) +        { +			menu_item->setVisible(TRUE); +			// A bit of a hack so we can remember that some UI element explicitly set this to be visible +			// so that some other UI element from multi-select doesn't later set this invisible. +			menu_item->pushVisible(TRUE); + +			menu_item->setEnabled(FALSE); +        } +    } +}  void hide_context_entries(LLMenuGL& menu,   						  const menuentry_vec_t &entries_to_show,  						  const menuentry_vec_t &disabled_entries) @@ -765,6 +806,31 @@ void LLInvFVBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  	hide_context_entries(menu, items, disabled_items);  } +bool get_selection_item_uuids(LLFolderView::selected_items_t& selected_items, uuid_vec_t& ids) +{ +	uuid_vec_t results; +    S32 non_item = 0; +	for(LLFolderView::selected_items_t::iterator it = selected_items.begin(); it != selected_items.end(); ++it) +	{ +		LLItemBridge *view_model = dynamic_cast<LLItemBridge *>((*it)->getViewModelItem()); + +		if(view_model && view_model->getUUID().notNull()) +		{ +			results.push_back(view_model->getUUID()); +		} +        else +        { +            non_item++; +        } +	} +	if (non_item == 0) +	{ +		ids = results; +		return true; +	} +	return false; +} +  void LLInvFVBridge::addTrashContextMenuOptions(menuentry_vec_t &items,  											   menuentry_vec_t &disabled_items)  { @@ -1120,7 +1186,7 @@ LLInvFVBridge* LLInvFVBridge::createBridge(LLAssetType::EType asset_type,  			{  				LL_WARNS() << LLAssetType::lookup(asset_type) << " asset has inventory type " << LLInventoryType::lookupHumanReadable(inv_type) << " on uuid " << uuid << LL_ENDL;  			} -			new_listener = new LLWearableBridge(inventory, root, uuid, asset_type, inv_type, (LLWearableType::EType)flags); +			new_listener = new LLWearableBridge(inventory, root, uuid, asset_type, inv_type, LLWearableType::inventoryFlagsToWearableType(flags));  			break;  		case LLAssetType::AT_CATEGORY:  			if (actual_asset_type == LLAssetType::AT_LINK_FOLDER) @@ -3585,7 +3651,7 @@ void LLFolderBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  	if(!model) return;  	buildContextMenuOptions(flags, items, disabled_items); -        hide_context_entries(menu, items, disabled_items); +    hide_context_entries(menu, items, disabled_items);  	// Reposition the menu, in case we're adding items to an existing menu.  	menu.needsArrange(); @@ -5770,7 +5836,7 @@ void LLWearableBridge::buildContextMenu(LLMenuGL& menu, U32 flags)  					if (LLWearableType::getAllowMultiwear(mWearableType))  					{  						items.push_back(std::string("Wearable Add")); -						if (gAgentWearables.getWearableCount(mWearableType) >= LLAgentWearables::MAX_CLOTHING_PER_TYPE) +						if (!gAgentWearables.canAddWearable(mWearableType))  						{  							disabled_items.push_back(std::string("Wearable Add"));  						} @@ -6439,4 +6505,22 @@ LLInvFVBridge* LLRecentInventoryBridgeBuilder::createBridge(  	return new_listener;  } +LLFolderViewGroupedItemBridge::LLFolderViewGroupedItemBridge() +{ +} + +void LLFolderViewGroupedItemBridge::groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu) +{ +    uuid_vec_t ids; +	menuentry_vec_t disabled_items; +    if (get_selection_item_uuids(selected_items, ids)) +    { +        if (!LLAppearanceMgr::instance().canAddWearables(ids)) +        { +			disabled_items.push_back(std::string("Wearable Add")); +        } +    } +	disable_context_entries_if_present(menu, disabled_items); +} +  // EOF diff --git a/indra/newview/llinventorybridge.h b/indra/newview/llinventorybridge.h index f8ef15991d..300cef7deb 100755 --- a/indra/newview/llinventorybridge.h +++ b/indra/newview/llinventorybridge.h @@ -37,6 +37,7 @@  #include "llviewerwearable.h"  #include "lltooldraganddrop.h"  #include "lllandmarklist.h" +#include "llfolderviewitem.h"  class LLInventoryFilter;  class LLInventoryPanel; @@ -689,4 +690,11 @@ void hide_context_entries(LLMenuGL& menu,  						  const menuentry_vec_t &entries_to_show,   						  const menuentry_vec_t &disabled_entries); +class LLFolderViewGroupedItemBridge: public LLFolderViewGroupedItemModel +{ +public: +    LLFolderViewGroupedItemBridge(); +    virtual void groupFilterContextMenu(folder_view_item_deque& selected_items, LLMenuGL& menu); +}; +  #endif // LL_LLINVENTORYBRIDGE_H diff --git a/indra/newview/llinventoryfunctions.cpp b/indra/newview/llinventoryfunctions.cpp index 1abc09bf3b..2546db546b 100755 --- a/indra/newview/llinventoryfunctions.cpp +++ b/indra/newview/llinventoryfunctions.cpp @@ -45,6 +45,7 @@  // newview includes  #include "llappearancemgr.h"  #include "llappviewer.h" +#include "llavataractions.h"  #include "llclipboard.h"  #include "lldonotdisturbnotificationstorage.h"  #include "llfloaterinventory.h" @@ -1114,16 +1115,35 @@ void LLInventoryAction::doToSelected(LLInventoryModel* model, LLFolderView* root  		LLFloater::setFloaterHost(multi_propertiesp);  	} -	std::set<LLFolderViewItem*>::iterator set_iter; - -	for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) -	{ -		LLFolderViewItem* folder_item = *set_iter; -		if(!folder_item) continue; -		LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); -		if(!bridge) continue; -		bridge->performAction(model, action); -	} +     +	std::set<LLUUID> selected_uuid_set = LLAvatarActions::getInventorySelectedUUIDs(); +    uuid_vec_t ids; +    std::copy(selected_uuid_set.begin(), selected_uuid_set.end(), std::back_inserter(ids)); +    // Check for actions that get handled in bulk +    if (action == "wear") +    { +        wear_multiple(ids, true); +    } +    else if (action == "wear_add") +    { +        wear_multiple(ids, false); +    } +    else if (action == "take_off" || action == "detach") +    { +        LLAppearanceMgr::instance().removeItemsFromAvatar(ids); +    } +    else +    { +        std::set<LLFolderViewItem*>::iterator set_iter; +        for (set_iter = selected_items.begin(); set_iter != selected_items.end(); ++set_iter) +        { +            LLFolderViewItem* folder_item = *set_iter; +            if(!folder_item) continue; +            LLInvFVBridge* bridge = (LLInvFVBridge*)folder_item->getViewModelItem(); +            if(!bridge) continue; +            bridge->performAction(model, action); +        } +    }  	LLFloater::setFloaterHost(NULL);  	if (multi_previewp) diff --git a/indra/newview/llinventoryicon.cpp b/indra/newview/llinventoryicon.cpp index b7c4ec6f8b..013a5a7629 100755 --- a/indra/newview/llinventoryicon.cpp +++ b/indra/newview/llinventoryicon.cpp @@ -183,6 +183,6 @@ const std::string& LLInventoryIcon::getIconName(LLInventoryType::EIconName idx)  LLInventoryType::EIconName LLInventoryIcon::assignWearableIcon(U32 misc_flag)  { -	const LLWearableType::EType wearable_type = LLWearableType::EType(LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK & misc_flag); +	const LLWearableType::EType wearable_type = LLWearableType::inventoryFlagsToWearableType(misc_flag);  	return LLWearableType::getIconName(wearable_type);  } diff --git a/indra/newview/llinventorypanel.cpp b/indra/newview/llinventorypanel.cpp index 3546317471..4a230accb6 100755 --- a/indra/newview/llinventorypanel.cpp +++ b/indra/newview/llinventorypanel.cpp @@ -146,7 +146,8 @@ LLInventoryPanel::LLInventoryPanel(const LLInventoryPanel::Params& p) :  	mShowEmptyMessage(p.show_empty_message),  	mViewsInitialized(false),  	mInvFVBridgeBuilder(NULL), -	mInventoryViewModel(p.name) +	mInventoryViewModel(p.name), +	mGroupedItemBridge(new LLFolderViewGroupedItemBridge)  {  	mInvFVBridgeBuilder = &INVENTORY_BRIDGE_BUILDER; @@ -186,6 +187,7 @@ LLFolderView * LLInventoryPanel::createFolderRoot(LLUUID root_id )  																	NULL,  																	root_id);      p.view_model = &mInventoryViewModel; +	p.grouped_item_model = mGroupedItemBridge;      p.use_label_suffix = mParams.use_label_suffix;      p.allow_multiselect = mAllowMultiSelect;      p.show_empty_message = mShowEmptyMessage; diff --git a/indra/newview/llinventorypanel.h b/indra/newview/llinventorypanel.h index a490dfce5d..bc4c10e441 100755 --- a/indra/newview/llinventorypanel.h +++ b/indra/newview/llinventorypanel.h @@ -43,6 +43,7 @@ class LLInvFVBridge;  class LLInventoryFolderViewModelBuilder;  class LLInvPanelComplObserver;  class LLFolderViewModelInventory; +class LLFolderViewGroupedItemBridge;  namespace LLInitParam  { @@ -240,6 +241,7 @@ protected:  	LLScrollContainer*			mScroller;  	LLFolderViewModelInventory	mInventoryViewModel; +    LLPointer<LLFolderViewGroupedItemBridge> mGroupedItemBridge;  	Params						mParams;	// stored copy of parameter block  	std::map<LLUUID, LLFolderViewItem*> mItemMap; diff --git a/indra/newview/lllocalbitmaps.cpp b/indra/newview/lllocalbitmaps.cpp index 8fb7550169..1380345164 100755 --- a/indra/newview/lllocalbitmaps.cpp +++ b/indra/newview/lllocalbitmaps.cpp @@ -545,12 +545,14 @@ void LLLocalBitmap::updateUserLayers(LLUUID old_id, LLUUID new_id, LLWearableTyp  					LLAvatarAppearanceDefines::ETextureIndex reg_texind = getTexIndex(type, baked_texind);  					if (reg_texind != LLAvatarAppearanceDefines::TEX_NUM_INDICES)  					{ -						U32 index = gAgentWearables.getWearableIndex(wearable); -						gAgentAvatarp->setLocalTexture(reg_texind, gTextureList.getImage(new_id), FALSE, index); -						gAgentAvatarp->wearableUpdated(type); - -						/* telling the manager to rebake once update cycle is fully done */ -						LLLocalBitmapMgr::setNeedsRebake(); +						U32 index; +						if (gAgentWearables.getWearableIndex(wearable,index)) +						{ +							gAgentAvatarp->setLocalTexture(reg_texind, gTextureList.getImage(new_id), FALSE, index); +							gAgentAvatarp->wearableUpdated(type); +							/* telling the manager to rebake once update cycle is fully done */ +							LLLocalBitmapMgr::setNeedsRebake(); +						}  					}  				} diff --git a/indra/newview/llmutelist.cpp b/indra/newview/llmutelist.cpp index 65ac11092c..88bfefa8b8 100755 --- a/indra/newview/llmutelist.cpp +++ b/indra/newview/llmutelist.cpp @@ -47,6 +47,7 @@  #include "pipeline.h"  #include <boost/tokenizer.hpp> +#include <boost/bind.hpp>  #include "lldispatcher.h"  #include "llxfermanager.h" @@ -146,22 +147,6 @@ std::string LLMute::getDisplayType() const  	}  } - -/* static */ -LLMuteList* LLMuteList::getInstance() -{ -	// Register callbacks at the first time that we find that the message system has been created. -	static BOOL registered = FALSE; -	if( !registered && gMessageSystem != NULL) -	{ -		registered = TRUE; -		// Register our various callbacks -		gMessageSystem->setHandlerFuncFast(_PREHASH_MuteListUpdate, processMuteListUpdate); -		gMessageSystem->setHandlerFuncFast(_PREHASH_UseCachedMuteList, processUseCachedMuteList); -	} -	return LLSingleton<LLMuteList>::getInstance(); // Call the "base" implementation. -} -  //-----------------------------------------------------------------------------  // LLMuteList()  //----------------------------------------------------------------------------- @@ -169,6 +154,18 @@ LLMuteList::LLMuteList() :  	mIsLoaded(FALSE)  {  	gGenericDispatcher.addHandler("emptymutelist", &sDispatchEmptyMuteList); + +	// Register our callbacks. We may be constructed before gMessageSystem, so +	// use callWhenReady() to register them as soon as gMessageSystem becomes +	// available. +	// When using bind(), must be explicit about default arguments such as +	// that last NULL. +	gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1, +											 _PREHASH_MuteListUpdate, processMuteListUpdate, +											 static_cast<void**>(NULL))); +	gMessageSystem.callWhenReady(boost::bind(&LLMessageSystem::setHandlerFuncFast, _1, +											 _PREHASH_UseCachedMuteList, processUseCachedMuteList, +											 static_cast<void**>(NULL)));  }  //----------------------------------------------------------------------------- diff --git a/indra/newview/llmutelist.h b/indra/newview/llmutelist.h index 3e998b4f0e..d315f225bf 100755 --- a/indra/newview/llmutelist.h +++ b/indra/newview/llmutelist.h @@ -84,11 +84,6 @@ public:  	LLMuteList();  	~LLMuteList(); -	// Implemented locally so that we can perform some delayed initialization.  -	// Callers should be careful to call this one and not LLSingleton<LLMuteList>::getInstance() -	// which would circumvent that mechanism. -MG -	static LLMuteList* getInstance(); -  	void addObserver(LLMuteListObserver* observer);  	void removeObserver(LLMuteListObserver* observer); diff --git a/indra/newview/llnavigationbar.h b/indra/newview/llnavigationbar.h index 7878bab24e..de12a39547 100755 --- a/indra/newview/llnavigationbar.h +++ b/indra/newview/llnavigationbar.h @@ -29,6 +29,7 @@  #include "llpanel.h"  #include "llbutton.h" +#include "llinitdestroyclass.h"  class LLLocationInputCtrl;  class LLMenuGL; diff --git a/indra/newview/llpaneleditwearable.cpp b/indra/newview/llpaneleditwearable.cpp index 19a86cdcea..9bd6007772 100755 --- a/indra/newview/llpaneleditwearable.cpp +++ b/indra/newview/llpaneleditwearable.cpp @@ -925,17 +925,17 @@ void LLPanelEditWearable::onCommitSexChange()          if (!isAgentAvatarValid()) return;          LLWearableType::EType type = mWearablePtr->getType(); -        U32 index = gAgentWearables.getWearableIndex(mWearablePtr); - -        if( !gAgentWearables.isWearableModifiable(type, index)) +        U32 index; +        if( !gAgentWearables.getWearableIndex(mWearablePtr, index) || +			!gAgentWearables.isWearableModifiable(type, index))          { -                return; +			return;          }          LLViewerVisualParam* param = static_cast<LLViewerVisualParam*>(gAgentAvatarp->getVisualParam( "male" ));          if( !param )          { -                return; +			return;          }          bool is_new_sex_male = (gSavedSettings.getU32("AvatarSex") ? SEX_MALE : SEX_FEMALE) == SEX_MALE; @@ -978,10 +978,17 @@ void LLPanelEditWearable::onTexturePickerCommit(const LLUICtrl* ctrl)                          }                          if (getWearable())                          { -                                U32 index = gAgentWearables.getWearableIndex(getWearable()); -                                gAgentAvatarp->setLocalTexture(entry->mTextureIndex, image, FALSE, index); -                                LLVisualParamHint::requestHintUpdates(); -                                gAgentAvatarp->wearableUpdated(type); +							U32 index; +							if (gAgentWearables.getWearableIndex(getWearable(), index)) +							{ +								gAgentAvatarp->setLocalTexture(entry->mTextureIndex, image, FALSE, index); +								LLVisualParamHint::requestHintUpdates(); +								gAgentAvatarp->wearableUpdated(type); +							} +							else +							{ +								LL_WARNS() << "wearable not found in gAgentWearables" << LL_ENDL; +							}                          }                  }                  else @@ -1058,7 +1065,12 @@ void LLPanelEditWearable::saveChanges(bool force_save_as)                  return;          } -        U32 index = gAgentWearables.getWearableIndex(mWearablePtr); +        U32 index; +		if (!gAgentWearables.getWearableIndex(mWearablePtr, index)) +		{ +			LL_WARNS() << "wearable not found" << LL_ENDL; +			return; +		}          std::string new_name = mNameEditor->getText(); @@ -1574,6 +1586,12 @@ void LLPanelEditWearable::onInvisibilityCommit(LLCheckBoxCtrl* checkbox_ctrl, LL          LL_INFOS() << "onInvisibilityCommit, self " << this << " checkbox_ctrl " << checkbox_ctrl << LL_ENDL; +		U32 index; +		if (!gAgentWearables.getWearableIndex(getWearable(),index)) +		{ +			LL_WARNS() << "wearable not found" << LL_ENDL; +			return; +		}          bool new_invis_state = checkbox_ctrl->get();          if (new_invis_state)          { @@ -1581,9 +1599,8 @@ void LLPanelEditWearable::onInvisibilityCommit(LLCheckBoxCtrl* checkbox_ctrl, LL                  mPreviousAlphaTexture[te] = lto->getID();                  LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture( IMG_INVISIBLE ); -                U32 index = gAgentWearables.getWearableIndex(getWearable()); -                gAgentAvatarp->setLocalTexture(te, image, FALSE, index); -                gAgentAvatarp->wearableUpdated(getWearable()->getType()); +				gAgentAvatarp->setLocalTexture(te, image, FALSE, index); +				gAgentAvatarp->wearableUpdated(getWearable()->getType());          }          else          { @@ -1598,7 +1615,6 @@ void LLPanelEditWearable::onInvisibilityCommit(LLCheckBoxCtrl* checkbox_ctrl, LL                  LLViewerFetchedTexture* image = LLViewerTextureManager::getFetchedTexture(prev_id);                  if (!image) return; -                U32 index = gAgentWearables.getWearableIndex(getWearable());                  gAgentAvatarp->setLocalTexture(te, image, FALSE, index);                  gAgentAvatarp->wearableUpdated(getWearable()->getType());          } diff --git a/indra/newview/llpanelmaininventory.cpp b/indra/newview/llpanelmaininventory.cpp index 17c0b226d0..06a7346f2a 100755 --- a/indra/newview/llpanelmaininventory.cpp +++ b/indra/newview/llpanelmaininventory.cpp @@ -1278,7 +1278,7 @@ void LLPanelMainInventory::setUploadCostIfNeeded()  		LLMenuItemBranchGL* upload_menu = mMenuAdd->findChild<LLMenuItemBranchGL>("upload");  		if(upload_menu)  		{ -			S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); +			S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();  			std::string cost_str;  			// getPriceUpload() returns -1 if no data available yet. diff --git a/indra/newview/llpanelsnapshotinventory.cpp b/indra/newview/llpanelsnapshotinventory.cpp index c8a201a5c8..48948a7567 100755 --- a/indra/newview/llpanelsnapshotinventory.cpp +++ b/indra/newview/llpanelsnapshotinventory.cpp @@ -78,7 +78,7 @@ BOOL LLPanelSnapshotInventory::postBuild()  // virtual  void LLPanelSnapshotInventory::onOpen(const LLSD& key)  { -	getChild<LLUICtrl>("hint_lbl")->setTextArg("[UPLOAD_COST]", llformat("%d", LLGlobalEconomy::Singleton::getInstance()->getPriceUpload())); +	getChild<LLUICtrl>("hint_lbl")->setTextArg("[UPLOAD_COST]", llformat("%d", LLGlobalEconomy::getInstance()->getPriceUpload()));  	LLPanelSnapshot::onOpen(key);  } diff --git a/indra/newview/llpanelsnapshotoptions.cpp b/indra/newview/llpanelsnapshotoptions.cpp index 0fc9ceec83..2a44674802 100755 --- a/indra/newview/llpanelsnapshotoptions.cpp +++ b/indra/newview/llpanelsnapshotoptions.cpp @@ -75,12 +75,12 @@ LLPanelSnapshotOptions::LLPanelSnapshotOptions()  	mCommitCallbackRegistrar.add("Snapshot.SendToFacebook",		boost::bind(&LLPanelSnapshotOptions::onSendToFacebook, this));  	mCommitCallbackRegistrar.add("Snapshot.SendToTwitter",		boost::bind(&LLPanelSnapshotOptions::onSendToTwitter, this));  	mCommitCallbackRegistrar.add("Snapshot.SendToFlickr",		boost::bind(&LLPanelSnapshotOptions::onSendToFlickr, this)); -	LLGlobalEconomy::Singleton::getInstance()->addObserver(this); +	LLGlobalEconomy::getInstance()->addObserver(this);  }  LLPanelSnapshotOptions::~LLPanelSnapshotOptions()  { -	LLGlobalEconomy::Singleton::getInstance()->removeObserver(this); +	LLGlobalEconomy::getInstance()->removeObserver(this);  }  // virtual @@ -97,7 +97,7 @@ void LLPanelSnapshotOptions::onOpen(const LLSD& key)  void LLPanelSnapshotOptions::updateUploadCost()  { -	S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); +	S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();  	getChild<LLUICtrl>("save_to_inventory_btn")->setLabelArg("[AMOUNT]", llformat("%d", upload_cost));  } diff --git a/indra/newview/llpaneltopinfobar.h b/indra/newview/llpaneltopinfobar.h index f37bd9c048..274d70c6f9 100755 --- a/indra/newview/llpaneltopinfobar.h +++ b/indra/newview/llpaneltopinfobar.h @@ -28,6 +28,7 @@  #define LLPANELTOPINFOBAR_H_  #include "llpanel.h" +#include "llinitdestroyclass.h"  class LLButton;  class LLTextBox; diff --git a/indra/newview/llsearchhistory.h b/indra/newview/llsearchhistory.h index 3309a8fcac..ade81675c2 100755 --- a/indra/newview/llsearchhistory.h +++ b/indra/newview/llsearchhistory.h @@ -28,6 +28,7 @@  #define LL_LLSEARCHHISTORY_H  #include "llsingleton.h" +#include "llinitdestroyclass.h"  #include "llui.h"  /** diff --git a/indra/newview/llsidepanelappearance.cpp b/indra/newview/llsidepanelappearance.cpp index 64f24cd291..ea7cf82674 100755 --- a/indra/newview/llsidepanelappearance.cpp +++ b/indra/newview/llsidepanelappearance.cpp @@ -212,7 +212,8 @@ void LLSidepanelAppearance::updateToVisibility(const LLSD &new_visibility)  			}  			if (is_wearable_edit_visible)  			{ -				if (gAgentWearables.getWearableIndex(wearable_ptr) == LLAgentWearables::MAX_CLOTHING_PER_TYPE) +				U32 index; +				if (!gAgentWearables.getWearableIndex(wearable_ptr,index))  				{  					// we're no longer wearing the wearable we were last editing, switch back to outfit editor  					showOutfitEditPanel(); diff --git a/indra/newview/llsnapshotlivepreview.cpp b/indra/newview/llsnapshotlivepreview.cpp index 0ae8a338e0..2e9df1f13f 100644 --- a/indra/newview/llsnapshotlivepreview.cpp +++ b/indra/newview/llsnapshotlivepreview.cpp @@ -1005,7 +1005,7 @@ void LLSnapshotLivePreview::saveTexture()  		std::string who_took_it;  		LLAgentUI::buildFullname(who_took_it);  		LLAssetStorage::LLStoreAssetCallback callback = NULL; -		S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); +		S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();  		void *userdata = NULL;  		upload_new_resource(tid,	// tid  			LLAssetType::AT_TEXTURE, diff --git a/indra/newview/llstartup.cpp b/indra/newview/llstartup.cpp index 42fc300187..3a85468bda 100755 --- a/indra/newview/llstartup.cpp +++ b/indra/newview/llstartup.cpp @@ -194,6 +194,7 @@  #include "llevents.h"  #include "llstartuplistener.h"  #include "lltoolbarview.h" +#include "llcleanup.h"  #if LL_WINDOWS  #include "lldxhardware.h" @@ -2826,7 +2827,7 @@ void LLStartUp::initNameCache()  void LLStartUp::cleanupNameCache()  { -	LLAvatarNameCache::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLAvatarNameCache);  	delete gCacheName;  	gCacheName = NULL; diff --git a/indra/newview/llsyswellwindow.h b/indra/newview/llsyswellwindow.h index 71b41476f5..cff3711bb9 100755 --- a/indra/newview/llsyswellwindow.h +++ b/indra/newview/llsyswellwindow.h @@ -32,6 +32,7 @@  #include "llscreenchannel.h"  #include "llsyswellitem.h"  #include "lltransientdockablefloater.h" +#include "llinitdestroyclass.h"  class LLAvatarName;  class LLChiclet; diff --git a/indra/newview/llviewerinventory.cpp b/indra/newview/llviewerinventory.cpp index d6c8ba10f6..d112118082 100755 --- a/indra/newview/llviewerinventory.cpp +++ b/indra/newview/llviewerinventory.cpp @@ -2106,7 +2106,7 @@ LLWearableType::EType LLViewerInventoryItem::getWearableType() const  	{  		return LLWearableType::WT_INVALID;  	} -	return LLWearableType::EType(getFlags() & LLInventoryItemFlags::II_FLAGS_WEARABLES_MASK); +	return LLWearableType::inventoryFlagsToWearableType(getFlags());  } diff --git a/indra/newview/llviewerinventory.h b/indra/newview/llviewerinventory.h index ca92565600..78b61d4fbc 100755 --- a/indra/newview/llviewerinventory.h +++ b/indra/newview/llviewerinventory.h @@ -30,7 +30,7 @@  #include "llinventory.h"  #include "llframetimer.h"  #include "llwearable.h" -#include "llui.h" //for LLDestroyClass +#include "llinitdestroyclass.h" //for LLDestroyClass  #include <boost/signals2.hpp>	// boost::signals2::trackable diff --git a/indra/newview/llviewermenu.cpp b/indra/newview/llviewermenu.cpp index 3b0adcf7f4..ee2abaab4b 100755 --- a/indra/newview/llviewermenu.cpp +++ b/indra/newview/llviewermenu.cpp @@ -130,6 +130,7 @@  #include "llpathfindingmanager.h"  #include "llstartup.h"  #include "boost/unordered_map.hpp" +#include "llcleanup.h"  using namespace LLAvatarAppearanceDefines; @@ -8416,7 +8417,7 @@ class LLWorldPostProcess : public view_listener_t  void handle_flush_name_caches()  { -	LLAvatarNameCache::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLAvatarNameCache);  	if (gCacheName) gCacheName->clear();  } @@ -8460,7 +8461,7 @@ class LLToggleUIHints : public view_listener_t  void LLUploadCostCalculator::calculateCost()  { -	S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); +	S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();  	// getPriceUpload() returns -1 if no data available yet.  	if(upload_cost >= 0) diff --git a/indra/newview/llviewermenufile.cpp b/indra/newview/llviewermenufile.cpp index f8e50ba463..deba6e130e 100755 --- a/indra/newview/llviewermenufile.cpp +++ b/indra/newview/llviewermenufile.cpp @@ -83,7 +83,7 @@ class LLFileEnableUpload : public view_listener_t  {  	bool handleEvent(const LLSD& userdata)  	{ -		bool new_value = gStatusBar && LLGlobalEconomy::Singleton::getInstance() && (gStatusBar->getBalance() >= LLGlobalEconomy::Singleton::getInstance()->getPriceUpload()); +		bool new_value = gStatusBar && LLGlobalEconomy::getInstance() && (gStatusBar->getBalance() >= LLGlobalEconomy::getInstance()->getPriceUpload());  		return new_value;  	}  }; @@ -433,7 +433,7 @@ class LLFileUploadBulk : public view_listener_t  			std::string display_name = LLStringUtil::null;  			LLAssetStorage::LLStoreAssetCallback callback = NULL; -			S32 expected_upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); +			S32 expected_upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();  			void *userdata = NULL;  			upload_new_resource( diff --git a/indra/newview/llviewermessage.cpp b/indra/newview/llviewermessage.cpp index 5cd92c9920..6f36317265 100755 --- a/indra/newview/llviewermessage.cpp +++ b/indra/newview/llviewermessage.cpp @@ -6222,9 +6222,9 @@ void process_frozen_message(LLMessageSystem *msgsystem, void **user_data)  // do some extra stuff once we get our economy data  void process_economy_data(LLMessageSystem *msg, void** /*user_data*/)  { -	LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::Singleton::getInstance()); +	LLGlobalEconomy::processEconomyData(msg, LLGlobalEconomy::getInstance()); -	S32 upload_cost = LLGlobalEconomy::Singleton::getInstance()->getPriceUpload(); +	S32 upload_cost = LLGlobalEconomy::getInstance()->getPriceUpload();  	LL_INFOS_ONCE("Messaging") << "EconomyData message arrived; upload cost is L$" << upload_cost << LL_ENDL; diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp index a2c0a91ea6..f463c620d3 100755 --- a/indra/newview/llviewerobject.cpp +++ b/indra/newview/llviewerobject.cpp @@ -102,6 +102,7 @@  #include "llmediaentry.h"  #include "llfloaterperms.h"  #include "llvocache.h" +#include "llcleanup.h"  //#define DEBUG_UPDATE_TYPE @@ -527,11 +528,11 @@ void LLViewerObject::initVOClasses()  void LLViewerObject::cleanupVOClasses()  { -	LLVOGrass::cleanupClass(); -	LLVOWater::cleanupClass(); -	LLVOTree::cleanupClass(); -	LLVOAvatar::cleanupClass(); -	LLVOVolume::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLVOGrass); +	SUBSYSTEM_CLEANUP(LLVOWater); +	SUBSYSTEM_CLEANUP(LLVOTree); +	SUBSYSTEM_CLEANUP(LLVOAvatar); +	SUBSYSTEM_CLEANUP(LLVOVolume);  	sObjectDataMap.clear();  } diff --git a/indra/newview/llviewerwindow.cpp b/indra/newview/llviewerwindow.cpp index e317989f04..12ff88c517 100755 --- a/indra/newview/llviewerwindow.cpp +++ b/indra/newview/llviewerwindow.cpp @@ -208,6 +208,7 @@  #include "llwindowlistener.h"  #include "llviewerwindowlistener.h"  #include "llpaneltopinfobar.h" +#include "llcleanup.h"  #if LL_WINDOWS  #include <tchar.h> // For Unicode conversion methods @@ -2124,7 +2125,7 @@ void LLViewerWindow::shutdownGL()  	// Shutdown GL cleanly.  Order is very important here.  	//--------------------------------------------------------  	LLFontGL::destroyDefaultFonts(); -	LLFontManager::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLFontManager);  	stop_glerror();  	gSky.cleanup(); @@ -2147,7 +2148,7 @@ void LLViewerWindow::shutdownGL()  	LLWorldMapView::cleanupTextures();  	LLViewerTextureManager::cleanup() ; -	LLImageGL::cleanupClass() ; +	SUBSYSTEM_CLEANUP(LLImageGL) ;  	LL_INFOS() << "All textures and llimagegl images are destroyed!" << LL_ENDL ; @@ -2160,7 +2161,7 @@ void LLViewerWindow::shutdownGL()  	gGL.shutdown(); -	LLVertexBuffer::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLVertexBuffer);  	LL_INFOS() << "LLVertexBuffer cleaned." << LL_ENDL ;  } diff --git a/indra/newview/llvoavatarself.cpp b/indra/newview/llvoavatarself.cpp index 1e9945b514..56997c928a 100755 --- a/indra/newview/llvoavatarself.cpp +++ b/indra/newview/llvoavatarself.cpp @@ -1570,8 +1570,16 @@ BOOL LLVOAvatarSelf::isTextureVisible(LLAvatarAppearanceDefines::ETextureIndex t  		return LLVOAvatar::isTextureVisible(type);  	} -	U32 index = gAgentWearables.getWearableIndex(wearable); -	return isTextureVisible(type,index); +	U32 index; +	if (gAgentWearables.getWearableIndex(wearable,index)) +	{ +		return isTextureVisible(type,index); +	} +	else +	{ +		LL_WARNS() << "Wearable not found" << LL_ENDL; +		return FALSE; +	}  }  bool LLVOAvatarSelf::areTexturesCurrent() const diff --git a/indra/newview/llwearableitemslist.cpp b/indra/newview/llwearableitemslist.cpp index fac0fd63ee..888ead0613 100755 --- a/indra/newview/llwearableitemslist.cpp +++ b/indra/newview/llwearableitemslist.cpp @@ -894,13 +894,13 @@ void LLWearableItemsList::ContextMenu::updateItemsVisibility(LLContextMenu* menu  	setMenuItemVisible(menu, "wear_wear", 			n_already_worn == 0 && n_worn == 0 && can_be_worn);  	setMenuItemEnabled(menu, "wear_wear", 			n_already_worn == 0 && n_worn == 0);  	setMenuItemVisible(menu, "wear_add",			wear_add_visible); -	setMenuItemEnabled(menu, "wear_add",			canAddWearables(ids)); +	setMenuItemEnabled(menu, "wear_add",			LLAppearanceMgr::instance().canAddWearables(ids));  	setMenuItemVisible(menu, "wear_replace",		n_worn == 0 && n_already_worn != 0 && can_be_worn);  	//visible only when one item selected and this item is worn  	setMenuItemVisible(menu, "edit",				!standalone && mask & (MASK_CLOTHING|MASK_BODYPART) && n_worn == n_items && n_worn == 1);  	setMenuItemEnabled(menu, "edit",				n_editable == 1 && n_worn == 1 && n_items == 1);  	setMenuItemVisible(menu, "create_new",			mask & (MASK_CLOTHING|MASK_BODYPART) && n_items == 1); -	setMenuItemEnabled(menu, "create_new",			canAddWearables(ids)); +	setMenuItemEnabled(menu, "create_new",			LLAppearanceMgr::instance().canAddWearables(ids));  	setMenuItemVisible(menu, "show_original",		!standalone);  	setMenuItemEnabled(menu, "show_original",		n_items == 1 && n_links == n_items);  	setMenuItemVisible(menu, "take_off",			mask == MASK_CLOTHING && n_worn == n_items); @@ -1004,65 +1004,4 @@ void LLWearableItemsList::ContextMenu::createNewWearable(const LLUUID& item_id)  	LLAgentWearables::createWearable(item->getWearableType(), true);  } -// Returns true if all the given objects and clothes can be added. -// static -bool LLWearableItemsList::ContextMenu::canAddWearables(const uuid_vec_t& item_ids) -{ -	// TODO: investigate wearables may not be loaded at this point EXT-8231 - -	U32 n_objects = 0; -	boost::unordered_map<LLWearableType::EType, U32> clothes_by_type; - -	// Count given clothes (by wearable type) and objects. -	for (uuid_vec_t::const_iterator it = item_ids.begin(); it != item_ids.end(); ++it) -	{ -		LLViewerInventoryItem* item = gInventory.getItem(*it); -		if (!item) -		{ -			return false; -		} - -		if (item->getType() == LLAssetType::AT_OBJECT) -		{ -			++n_objects; -		} -		else if (item->getType() == LLAssetType::AT_CLOTHING) -		{ -			++clothes_by_type[item->getWearableType()]; -		} -		else -		{ -			LL_WARNS() << "Unexpected wearable type" << LL_ENDL; -			return false; -		} -	} - -	// Check whether we can add all the objects. -	if (!isAgentAvatarValid() || !gAgentAvatarp->canAttachMoreObjects(n_objects)) -	{ -		return false; -	} - -	// Check whether we can add all the clothes. -	boost::unordered_map<LLWearableType::EType, U32>::const_iterator m_it; -	for (m_it = clothes_by_type.begin(); m_it != clothes_by_type.end(); ++m_it) -	{ -		LLWearableType::EType w_type	= m_it->first; -		U32 n_clothes					= m_it->second; - -		U32 wearable_count = gAgentWearables.getWearableCount(w_type); -		if ((wearable_count > 0) && !LLWearableType::getAllowMultiwear(w_type)) -		{ -			return false; -		} -		if ((wearable_count + n_clothes) > LLAgentWearables::MAX_CLOTHING_PER_TYPE) -		{ -			return false; -		} - -	} - -	return true; -} -  // EOF diff --git a/indra/newview/llwearableitemslist.h b/indra/newview/llwearableitemslist.h index c731a7d6cf..e6788ab249 100755 --- a/indra/newview/llwearableitemslist.h +++ b/indra/newview/llwearableitemslist.h @@ -429,7 +429,6 @@ public:  		static void setMenuItemEnabled(LLContextMenu* menu, const std::string& name, bool val);  		static void updateMask(U32& mask, LLAssetType::EType at);  		static void createNewWearable(const LLUUID& item_id); -		static bool canAddWearables(const uuid_vec_t& item_ids);  		LLWearableItemsList*	mParent;  	}; diff --git a/indra/newview/pipeline.cpp b/indra/newview/pipeline.cpp index 03712c1065..9c1b78626f 100755 --- a/indra/newview/pipeline.cpp +++ b/indra/newview/pipeline.cpp @@ -115,6 +115,7 @@  #include "llpathfindingpathtool.h"  #include "llscenemonitor.h"  #include "llprogressview.h" +#include "llcleanup.h"  #ifdef _DEBUG  // Debug indices is disabled for now for debug performance - djs 4/24/02 @@ -7373,7 +7374,7 @@ void LLPipeline::doResetVertexBuffers(bool forced)  	}  	LLVOPartGroup::destroyGL(); -	LLVertexBuffer::cleanupClass(); +	SUBSYSTEM_CLEANUP(LLVertexBuffer);  	//delete all name pool caches  	LLGLNamePool::cleanupPools(); diff --git a/indra/newview/tests/llremoteparcelrequest_test.cpp b/indra/newview/tests/llremoteparcelrequest_test.cpp index c49b0350e9..5e3649fdae 100755 --- a/indra/newview/tests/llremoteparcelrequest_test.cpp +++ b/indra/newview/tests/llremoteparcelrequest_test.cpp @@ -33,6 +33,7 @@  #include "../llagent.h"  #include "message.h"  #include "llurlentry.h" +#include "llpounceable.h"  namespace {  	const LLUUID TEST_PARCEL_ID("11111111-1111-1111-1111-111111111111"); @@ -61,7 +62,7 @@ void LLMessageSystem::addUUID(char const *,LLUUID const &) { }  void LLMessageSystem::addUUIDFast(char const *,LLUUID const &) { }  void LLMessageSystem::nextBlockFast(char const *) { }  void LLMessageSystem::newMessage(char const *) { } -LLMessageSystem * gMessageSystem; +LLPounceable<LLMessageSystem*, LLPounceableStatic> gMessageSystem;  char const* const _PREHASH_AgentID = 0;   // never dereferenced during this test  char const* const _PREHASH_AgentData = 0; // never dereferenced during this test  LLAgent gAgent; diff --git a/indra/test/message_tut.cpp b/indra/test/message_tut.cpp index aa23699de0..b9c025f518 100755 --- a/indra/test/message_tut.cpp +++ b/indra/test/message_tut.cpp @@ -103,7 +103,7 @@ namespace tut  		~LLMessageSystemTestData()  		{  			// not end_messaging_system() -			delete gMessageSystem; +			delete static_cast<LLMessageSystem*>(gMessageSystem);  			gMessageSystem = NULL;  			// rm contents of temp dir | 
