summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-x.hgtags1
-rwxr-xr-xindra/integration_tests/llimage_libtest/llimage_libtest.cpp3
-rwxr-xr-xindra/llappearance/llwearabledata.cpp59
-rwxr-xr-x[-rw-r--r--]indra/llappearance/llwearabledata.h14
-rwxr-xr-x[-rw-r--r--]indra/llappearance/llwearabletype.cpp9
-rwxr-xr-x[-rw-r--r--]indra/llappearance/llwearabletype.h1
-rwxr-xr-xindra/llcommon/CMakeLists.txt3
-rwxr-xr-xindra/llcommon/llapp.cpp3
-rw-r--r--indra/llcommon/llcleanup.h30
-rwxr-xr-xindra/llcommon/llcommon.cpp5
-rwxr-xr-xindra/llcommon/llerror.cpp5
-rwxr-xr-xindra/llcommon/llerrorcontrol.h5
-rw-r--r--indra/llcommon/llinitdestroyclass.h190
-rw-r--r--indra/llcommon/llpounceable.h215
-rwxr-xr-xindra/llcommon/llregistry.h2
-rwxr-xr-xindra/llcommon/llsingleton.cpp321
-rwxr-xr-xindra/llcommon/llsingleton.h530
-rw-r--r--indra/llcommon/tests/llpounceable_test.cpp230
-rwxr-xr-xindra/llcommon/tests/llsingleton_test.cpp1
-rwxr-xr-xindra/llcorehttp/tests/llcorehttp_test.cpp3
-rwxr-xr-xindra/llcrashlogger/llcrashlogger.cpp3
-rwxr-xr-xindra/llinventory/lleconomy.h9
-rwxr-xr-xindra/llmessage/llhttpclientadapter.h3
-rwxr-xr-xindra/llmessage/message.cpp7
-rwxr-xr-xindra/llmessage/message.h3
-rwxr-xr-xindra/llmessage/tests/llhttpclient_test.cpp3
-rwxr-xr-xindra/llmessage/tests/lltemplatemessagedispatcher_test.cpp3
-rwxr-xr-xindra/llmessage/tests/lltrustedmessageservice_test.cpp3
-rwxr-xr-xindra/llui/llfolderview.cpp12
-rwxr-xr-xindra/llui/llfolderview.h8
-rwxr-xr-x[-rw-r--r--]indra/llui/llfolderviewitem.h7
-rwxr-xr-xindra/llui/llspellcheck.h1
-rwxr-xr-xindra/llui/llui.cpp3
-rwxr-xr-xindra/llui/llui.h89
-rwxr-xr-xindra/llui/tests/llurlentry_stub.cpp3
-rwxr-xr-xindra/lscript/lscript_compile/indra.l2
-rwxr-xr-xindra/lscript/lscript_compile/indra.y6
-rw-r--r--indra/newview/VIEWER_VERSION.txt2
-rwxr-xr-xindra/newview/llagentwearables.cpp23
-rwxr-xr-xindra/newview/llagentwearables.h1
-rwxr-xr-xindra/newview/llappearancemgr.cpp314
-rwxr-xr-xindra/newview/llappearancemgr.h15
-rwxr-xr-xindra/newview/llappviewer.cpp43
-rwxr-xr-xindra/newview/llassetuploadresponders.cpp6
-rwxr-xr-xindra/newview/llfavoritesbar.h1
-rwxr-xr-xindra/newview/llfloaterbvhpreview.cpp2
-rwxr-xr-xindra/newview/llfloaterimcontainer.cpp2
-rwxr-xr-xindra/newview/llfloaternamedesc.cpp4
-rwxr-xr-xindra/newview/llhints.h1
-rwxr-xr-xindra/newview/llimview.h1
-rwxr-xr-xindra/newview/llinventorybridge.cpp90
-rwxr-xr-xindra/newview/llinventorybridge.h8
-rwxr-xr-xindra/newview/llinventoryfunctions.cpp40
-rwxr-xr-xindra/newview/llinventoryicon.cpp2
-rwxr-xr-xindra/newview/llinventorypanel.cpp4
-rwxr-xr-xindra/newview/llinventorypanel.h2
-rwxr-xr-xindra/newview/lllocalbitmaps.cpp14
-rwxr-xr-xindra/newview/llmutelist.cpp29
-rwxr-xr-xindra/newview/llmutelist.h5
-rwxr-xr-xindra/newview/llnavigationbar.h1
-rwxr-xr-xindra/newview/llpaneleditwearable.cpp44
-rwxr-xr-xindra/newview/llpanelmaininventory.cpp2
-rwxr-xr-xindra/newview/llpanelsnapshotinventory.cpp2
-rwxr-xr-xindra/newview/llpanelsnapshotoptions.cpp6
-rwxr-xr-xindra/newview/llpaneltopinfobar.h1
-rwxr-xr-xindra/newview/llsearchhistory.h1
-rwxr-xr-xindra/newview/llsidepanelappearance.cpp3
-rw-r--r--indra/newview/llsnapshotlivepreview.cpp2
-rwxr-xr-xindra/newview/llstartup.cpp3
-rwxr-xr-xindra/newview/llsyswellwindow.h1
-rwxr-xr-xindra/newview/llviewerinventory.cpp2
-rwxr-xr-xindra/newview/llviewerinventory.h2
-rwxr-xr-xindra/newview/llviewermenu.cpp5
-rwxr-xr-xindra/newview/llviewermenufile.cpp4
-rwxr-xr-xindra/newview/llviewermessage.cpp4
-rwxr-xr-xindra/newview/llviewerobject.cpp11
-rwxr-xr-xindra/newview/llviewerwindow.cpp7
-rwxr-xr-xindra/newview/llvoavatarself.cpp12
-rwxr-xr-xindra/newview/llwearableitemslist.cpp65
-rwxr-xr-xindra/newview/llwearableitemslist.h1
-rwxr-xr-xindra/newview/pipeline.cpp3
-rwxr-xr-xindra/newview/tests/llremoteparcelrequest_test.cpp3
-rwxr-xr-xindra/test/message_tut.cpp2
83 files changed, 1971 insertions, 620 deletions
diff --git a/.hgtags b/.hgtags
index cc88ee1664..8db85d9611 100755
--- a/.hgtags
+++ b/.hgtags
@@ -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