summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/always_return.h16
-rw-r--r--indra/llcommon/llcoros.cpp55
-rw-r--r--indra/llcommon/llexception.cpp44
-rw-r--r--indra/llcommon/llexception.h114
-rw-r--r--indra/llcommon/tests/llleap_test.cpp2
-rw-r--r--indra/llcommon/tests/llstring_test.cpp4
-rw-r--r--indra/llwindow/llwindowsdl.cpp15
-rw-r--r--indra/llwindow/llwindowsdl.h2
-rw-r--r--indra/llwindow/llwindowwin32.cpp13
-rw-r--r--indra/llxml/llxmltree.cpp19
-rw-r--r--indra/newview/llappviewerwin32.cpp36
-rw-r--r--indra/newview/llfeaturemanager.cpp36
-rw-r--r--indra/newview/llfloaterperformance.cpp12
-rw-r--r--indra/newview/llfloaterperformance.h2
-rw-r--r--indra/newview/llviewerobject.cpp160
-rw-r--r--indra/newview/llviewerobject.h2
-rwxr-xr-xindra/newview/llviewerregion.cpp1
-rw-r--r--indra/test/test.cpp75
18 files changed, 416 insertions, 192 deletions
diff --git a/indra/llcommon/always_return.h b/indra/llcommon/always_return.h
index a206471da5..b99eb49096 100644
--- a/indra/llcommon/always_return.h
+++ b/indra/llcommon/always_return.h
@@ -79,6 +79,22 @@ namespace LL
DESIRED mDefault;
};
+ // specialize for AlwaysReturn<void>
+ template <>
+ struct AlwaysReturn<void>
+ {
+ public:
+ AlwaysReturn() {}
+
+ // callable returns a type not convertible to DESIRED, return default
+ template <typename CALLABLE, typename... ARGS>
+ void operator()(CALLABLE&& callable, ARGS&&... args)
+ {
+ // discard whatever callable(args) returns
+ std::forward<CALLABLE>(callable)(std::forward<ARGS>(args)...);
+ }
+ };
+
/**
* always_return<T>(some_function, some_args...) calls
* some_function(some_args...). It is guaranteed to return a value of type
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index aa8eca7d90..7512bac88d 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -123,7 +123,7 @@ LLCoros::LLCoros():
// Previously we used
// boost::context::guarded_stack_allocator::default_stacksize();
// empirically this is insufficient.
- mStackSize(1024*1024),
+ mStackSize(512*1024),
// mCurrent does NOT own the current CoroData instance -- it simply
// points to it. So initialize it with a no-op deleter.
mCurrent{ [](CoroData*){} }
@@ -155,7 +155,7 @@ void LLCoros::cleanupSingleton()
// don't use llcoro::suspend() because that module depends
// on this one
// This will yield current(main) thread and will let active
- // corutines run once
+ // coroutines run once
boost::this_fiber::yield();
}
printActiveCoroutines("after pumping");
@@ -286,55 +286,6 @@ std::string LLCoros::launch(const std::string& prefix, const callable_t& callabl
return name;
}
-namespace
-{
-
-#if LL_WINDOWS
-
-static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
-
-U32 exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop)
-{
- if (code == STATUS_MSC_EXCEPTION)
- {
- // C++ exception, go on
- return EXCEPTION_CONTINUE_SEARCH;
- }
- else
- {
- // handle it
- return EXCEPTION_EXECUTE_HANDLER;
- }
-}
-
-void sehandle(const LLCoros::callable_t& callable)
-{
- __try
- {
- callable();
- }
- __except (exception_filter(GetExceptionCode(), GetExceptionInformation()))
- {
- // convert to C++ styled exception
- // Note: it might be better to use _se_set_translator
- // if you want exception to inherit full callstack
- char integer_string[512];
- sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
- throw std::exception(integer_string);
- }
-}
-
-#else // ! LL_WINDOWS
-
-inline void sehandle(const LLCoros::callable_t& callable)
-{
- callable();
-}
-
-#endif // ! LL_WINDOWS
-
-} // anonymous namespace
-
// Top-level wrapper around caller's coroutine callable.
// Normally we like to pass strings and such by const reference -- but in this
// case, we WANT to copy both the name and the callable to our local stack!
@@ -348,7 +299,7 @@ void LLCoros::toplevel(std::string name, callable_t callable)
// run the code the caller actually wants in the coroutine
try
{
- sehandle(callable);
+ LL::seh::catcher(callable);
}
catch (const Stop& exc)
{
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp
index c0154a569f..107fdc2b2d 100644
--- a/indra/llcommon/llexception.cpp
+++ b/indra/llcommon/llexception.cpp
@@ -15,7 +15,12 @@
#include "llexception.h"
// STL headers
// std headers
+#include <iomanip>
+#include <sstream>
#include <typeinfo>
+#if LL_WINDOWS
+#include <excpt.h>
+#endif // LL_WINDOWS
// external library headers
#include <boost/exception/diagnostic_information.hpp>
#include <boost/exception/error_info.hpp>
@@ -29,7 +34,6 @@
// On Windows, header-only implementation causes macro collisions -- use
// prebuilt library
#define BOOST_STACKTRACE_LINK
-#include <excpt.h>
#endif // LL_WINDOWS
#include <boost/stacktrace.hpp>
@@ -94,25 +98,47 @@ void annotate_exception_(boost::exception& exc)
// For windows SEH exception handling we sometimes need a filter that will
// separate C++ exceptions from C SEH exceptions
-static const U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
+static constexpr U32 STATUS_MSC_EXCEPTION = 0xE06D7363; // compiler specific
+static constexpr U32 STATUS_STACK_FULL = 0xC00000FD;
-U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop)
+void LL::seh::fill_stacktrace(std::string& stacktrace, U32 code)
{
- const auto stack = to_string(boost::stacktrace::stacktrace());
- LL_WARNS() << "SEH Exception handled (that probably shouldn't be): Code " << code
- << "\n Stack trace: \n"
- << stack << LL_ENDL;
+ // Sadly, despite its diagnostic importance, trying to capture a
+ // stacktrace when the stack is already blown only terminates us faster.
+ if (code == STATUS_STACK_FULL)
+ {
+ stacktrace = "(stack overflow, no traceback)";
+ }
+ else
+ {
+ stacktrace = to_string(boost::stacktrace::stacktrace());
+ }
+}
+U32 LL::seh::common_filter(U32 code, struct _EXCEPTION_POINTERS*)
+{
if (code == STATUS_MSC_EXCEPTION)
{
- // C++ exception, go on
+ // C++ exception, don't stop at this handler
return EXCEPTION_CONTINUE_SEARCH;
}
else
{
- // handle it
+ // This is a non-C++ exception, e.g. hardware check.
+ // Pass control into the handler block.
return EXCEPTION_EXECUTE_HANDLER;
}
}
+void LL::seh::rethrow(U32 code, const std::string& stacktrace)
+{
+ std::ostringstream out;
+ out << "Windows exception 0x" << std::hex << code;
+ if (! stacktrace.empty())
+ {
+ out << '\n' << stacktrace;
+ }
+ LLTHROW(Windows_SEH_exception(out.str()));
+}
+
#endif //LL_WINDOWS
diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h
index 68e609444e..f58a553eb3 100644
--- a/indra/llcommon/llexception.h
+++ b/indra/llcommon/llexception.h
@@ -12,6 +12,7 @@
#if ! defined(LL_LLEXCEPTION_H)
#define LL_LLEXCEPTION_H
+#include "always_return.h"
#include <stdexcept>
#include <boost/exception/exception.hpp>
#include <boost/throw_exception.hpp>
@@ -102,14 +103,115 @@ void crash_on_unhandled_exception_(const char*, int, const char*, const std::str
log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT)
void log_unhandled_exception_(const char*, int, const char*, const std::string&);
+/*****************************************************************************
+* Structured Exception Handling
+*****************************************************************************/
+// this is used in platform-generic code -- define outside #if LL_WINDOWS
+struct Windows_SEH_exception: public LLException
+{
+ Windows_SEH_exception(const std::string& what): LLException(what) {}
+};
+
+namespace LL
+{
+namespace seh
+{
+
+#if LL_WINDOWS //-------------------------------------------------------------
+
+void fill_stacktrace(std::string& stacktrace, U32 code);
+
+// wrapper around caller's U32 filter(U32 code, struct _EXCEPTION_POINTERS*)
+// filter function: capture a stacktrace, if possible, before forwarding the
+// call to the caller's filter() function
+template <typename FILTER>
+U32 filter_(std::string& stacktrace, FILTER&& filter,
+ U32 code, struct _EXCEPTION_POINTERS* exptrs)
+{
+ // By the time the handler gets control, the stack has been unwound,
+ // so report the stack trace now at filter() time.
+ fill_stacktrace(stacktrace, code);
+ return std::forward<FILTER>(filter)(code, exptrs);
+}
+
+template <typename TRYCODE, typename FILTER, typename HANDLER>
+auto catcher_inner(std::string& stacktrace,
+ TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler)
+{
+ __try
+ {
+ return std::forward<TRYCODE>(trycode)();
+ }
+ __except (filter_(stacktrace,
+ std::forward<FILTER>(filter),
+ GetExceptionCode(), GetExceptionInformation()))
+ {
+ return always_return<decltype(trycode())>(
+ std::forward<HANDLER>(handler), GetExceptionCode(), stacktrace);
+ }
+}
+
+// triadic variant specifies try(), filter(U32, struct _EXCEPTION_POINTERS*),
+// handler(U32, const std::string& stacktrace)
+// stacktrace may or may not be available
+template <typename TRYCODE, typename FILTER, typename HANDLER>
+auto catcher(TRYCODE&& trycode, FILTER&& filter, HANDLER&& handler)
+{
+ // Construct and destroy this stacktrace string in the outer function
+ // because we can't do either in the function with __try/__except.
+ std::string stacktrace;
+ return catcher_inner(stacktrace,
+ std::forward<TRYCODE>(trycode),
+ std::forward<FILTER>(filter),
+ std::forward<HANDLER>(handler));
+}
-#if LL_WINDOWS
+// common_filter() handles the typical case in which we want our handler
+// clause to handle only Structured Exceptions rather than explicitly-thrown
+// C++ exceptions
+U32 common_filter(U32 code, struct _EXCEPTION_POINTERS*);
+
+// dyadic variant specifies try(), handler(U32, stacktrace), assumes common_filter()
+template <typename TRYCODE, typename HANDLER>
+auto catcher(TRYCODE&& trycode, HANDLER&& handler)
+{
+ return catcher(std::forward<TRYCODE>(trycode),
+ common_filter,
+ std::forward<HANDLER>(handler));
+}
+
+// monadic variant specifies try(), assumes default filter and handler
+template <typename TRYCODE>
+auto catcher(TRYCODE&& trycode)
+{
+ return catcher(std::forward<TRYCODE>(trycode), rethrow);
+}
+
+[[noreturn]] void rethrow(U32 code, const std::string& stacktrace);
+
+#else // not LL_WINDOWS -----------------------------------------------------
+
+template <typename TRYCODE, typename FILTER, typename HANDLER>
+auto catcher(TRYCODE&& trycode, FILTER&&, HANDLER&&)
+{
+ return std::forward<TRYCODE>(trycode)();
+}
+
+template <typename TRYCODE, typename HANDLER>
+auto catcher(TRYCODE&& trycode, HANDLER&&)
+{
+ return std::forward<TRYCODE>(trycode)();
+}
+
+template <typename TRYCODE>
+auto catcher(TRYCODE&& trycode)
+{
+ return std::forward<TRYCODE>(trycode)();
+}
-// SEH exception filtering for use in __try __except
-// Separates C++ exceptions from C SEH exceptions
-// Todo: might be good idea to do some kind of seh_to_msc_wrapper(function, ARGS&&);
-U32 msc_exception_filter(U32 code, struct _EXCEPTION_POINTERS *exception_infop);
+#endif // not LL_WINDOWS -----------------------------------------------------
-#endif //LL_WINDOWS
+} // namespace LL::seh
+} // namespace LL
#endif /* ! defined(LL_LLEXCEPTION_H) */
diff --git a/indra/llcommon/tests/llleap_test.cpp b/indra/llcommon/tests/llleap_test.cpp
index fa48bcdefd..3fb25b4cef 100644
--- a/indra/llcommon/tests/llleap_test.cpp
+++ b/indra/llcommon/tests/llleap_test.cpp
@@ -35,7 +35,7 @@
// causes Windows abdominal pain such that it later fails code-signing in some
// mysterious way. Entirely suppressing these LLLeap tests pushes the failure
// rate MUCH lower. Can we re-enable them with a smaller data size on Windows?
-const size_t BUFFERED_LENGTH = 100*1024;
+const size_t BUFFERED_LENGTH = 1023*1024;
#else // not Windows
const size_t BUFFERED_LENGTH = 1023*1024; // try wrangling just under a megabyte of data
diff --git a/indra/llcommon/tests/llstring_test.cpp b/indra/llcommon/tests/llstring_test.cpp
index 3fadfa5334..ea5b0ee5fc 100644
--- a/indra/llcommon/tests/llstring_test.cpp
+++ b/indra/llcommon/tests/llstring_test.cpp
@@ -377,7 +377,7 @@ namespace tut
{
F32 value;
std::string str_val("2147483647"); //0x7FFFFFFF
- ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647);
+ ensure("1: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 2147483647.f);
str_val = "0";
ensure("2: convertToF32 failed", LLStringUtil::convertToF32(str_val, value) && value == 0);
@@ -399,7 +399,7 @@ namespace tut
{
F64 value;
std::string str_val("9223372036854775807"); //0x7FFFFFFFFFFFFFFF
- ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807LL);
+ ensure("1: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 9223372036854775807.);
str_val = "0";
ensure("2: convertToF64 failed", LLStringUtil::convertToF64(str_val, value) && value == 0.0F);
diff --git a/indra/llwindow/llwindowsdl.cpp b/indra/llwindow/llwindowsdl.cpp
index 255d89af77..218f953c7f 100644
--- a/indra/llwindow/llwindowsdl.cpp
+++ b/indra/llwindow/llwindowsdl.cpp
@@ -647,6 +647,7 @@ BOOL LLWindowSDL::createContext(int x, int y, int width, int height, int bits, B
{
{SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR,"0"},
{SDL_HINT_MOUSE_FOCUS_CLICKTHROUGH,"1"},
+ {SDL_HINT_IME_INTERNAL_EDITING,"1"}
};
for( auto hint: hintList )
@@ -2619,3 +2620,17 @@ U32 LLWindowSDL::getAvailableVRAMMegabytes()
{
return 4096;
}
+
+void LLWindowSDL::setLanguageTextInput(const LLCoordGL& position)
+{
+ LLCoordWindow win_pos;
+ convertCoords( position, &win_pos );
+
+ SDL_Rect r;
+ r.x = win_pos.mX;
+ r.y = win_pos.mY;
+ r.w = 500;
+ r.h = 16;
+
+ SDL_SetTextInputRect(&r);
+}
diff --git a/indra/llwindow/llwindowsdl.h b/indra/llwindow/llwindowsdl.h
index e922ce592c..d24739cbda 100644
--- a/indra/llwindow/llwindowsdl.h
+++ b/indra/llwindow/llwindowsdl.h
@@ -171,6 +171,8 @@ public:
void *getPlatformWindow() override;
void bringToFront() override;
+
+ void setLanguageTextInput(const LLCoordGL& pos) override;
void spawnWebBrowser(const std::string &escaped_url, bool async) override;
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index d6b93b93d9..12cd5320b8 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -163,18 +163,7 @@ HGLRC SafeCreateContext(HDC &hdc)
GLuint SafeChoosePixelFormat(HDC &hdc, const PIXELFORMATDESCRIPTOR *ppfd)
{
- __try
- {
- return ChoosePixelFormat(hdc, ppfd);
- }
- __except (EXCEPTION_EXECUTE_HANDLER)
- {
- // convert to C++ styled exception
- // C exception don't allow classes, so it's a regular char array
- char integer_string[32];
- sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
- throw std::exception(integer_string);
- }
+ return LL::seh::catcher([hdc, ppfd]{ return ChoosePixelFormat(hdc, ppfd); });
}
//static
diff --git a/indra/llxml/llxmltree.cpp b/indra/llxml/llxmltree.cpp
index baf2e6a951..cd41aa6f2d 100644
--- a/indra/llxml/llxmltree.cpp
+++ b/indra/llxml/llxmltree.cpp
@@ -108,14 +108,17 @@ LLXmlTreeNode::LLXmlTreeNode( const std::string& name, LLXmlTreeNode* parent, LL
LLXmlTreeNode::~LLXmlTreeNode()
{
- attribute_map_t::iterator iter;
- for (iter=mAttributes.begin(); iter != mAttributes.end(); iter++)
- delete iter->second;
- for(LLXmlTreeNode* node : mChildren)
- {
- delete node;
- }
- mChildren.clear();
+ for (auto& attr : mAttributes)
+ {
+ delete attr.second;
+ }
+ mAttributes.clear();
+
+ for (auto& child : mChildren)
+ {
+ delete child;
+ }
+ mChildren.clear();
}
void LLXmlTreeNode::dump( const std::string& prefix )
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 4c90a82fcb..a13e4de308 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -400,17 +400,10 @@ void ll_nvapi_init(NvDRSSessionHandle hSession)
}
}
-//#define DEBUGGING_SEH_FILTER 1
-#if DEBUGGING_SEH_FILTER
-# define WINMAIN DebuggingWinMain
-#else
-# define WINMAIN wWinMain
-#endif
-
-int APIENTRY WINMAIN(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- PWSTR pCmdLine,
- int nCmdShow)
+int APIENTRY wWinMain(HINSTANCE hInstance,
+ HINSTANCE hPrevInstance,
+ PWSTR pCmdLine,
+ int nCmdShow)
{
// Call Tracy first thing to have it allocate memory
// https://github.com/wolfpld/tracy/issues/196
@@ -559,27 +552,6 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
return 0;
}
-#if DEBUGGING_SEH_FILTER
-// The compiler doesn't like it when you use __try/__except blocks
-// in a method that uses object destructors. Go figure.
-// This winmain just calls the real winmain inside __try.
-// The __except calls our exception filter function. For debugging purposes.
-int APIENTRY wWinMain(HINSTANCE hInstance,
- HINSTANCE hPrevInstance,
- PWSTR lpCmdLine,
- int nCmdShow)
-{
- __try
- {
- WINMAIN(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
- }
- __except( viewer_windows_exception_handler( GetExceptionInformation() ) )
- {
- _tprintf( _T("Exception handled.\n") );
- }
-}
-#endif
-
void LLAppViewerWin32::disableWinErrorReporting()
{
std::string executable_name = gDirUtilp->getExecutableFilename();
diff --git a/indra/newview/llfeaturemanager.cpp b/indra/newview/llfeaturemanager.cpp
index 99139e6528..765599bb82 100644
--- a/indra/newview/llfeaturemanager.cpp
+++ b/indra/newview/llfeaturemanager.cpp
@@ -40,6 +40,7 @@
#include "llappviewer.h"
#include "llbufferstream.h"
+#include "llexception.h"
#include "llnotificationsutil.h"
#include "llviewercontrol.h"
#include "llworld.h"
@@ -377,33 +378,6 @@ bool LLFeatureManager::parseFeatureTable(std::string filename)
F32 gpu_benchmark();
-#if LL_WINDOWS
-
-F32 logExceptionBenchmark()
-{
- // FIXME: gpu_benchmark uses many C++ classes on the stack to control state.
- // SEH exceptions with our current exception handling options do not call
- // destructors for these classes, resulting in an undefined state should
- // this handler be invoked.
- F32 gbps = -1;
- __try
- {
- gbps = gpu_benchmark();
- }
- __except (msc_exception_filter(GetExceptionCode(), GetExceptionInformation()))
- {
- // HACK - ensure that profiling is disabled
- LLGLSLShader::finishProfile(false);
-
- // convert to C++ styled exception
- char integer_string[32];
- sprintf(integer_string, "SEH, code: %lu\n", GetExceptionCode());
- throw std::exception(integer_string);
- }
- return gbps;
-}
-#endif
-
bool LLFeatureManager::loadGPUClass()
{
if (!gSavedSettings.getBOOL("SkipBenchmark"))
@@ -413,14 +387,12 @@ bool LLFeatureManager::loadGPUClass()
F32 gbps;
try
{
-#if LL_WINDOWS
- gbps = logExceptionBenchmark();
-#else
- gbps = gpu_benchmark();
-#endif
+ gbps = LL::seh::catcher(gpu_benchmark);
}
catch (const std::exception& e)
{
+ // HACK - ensure that profiling is disabled
+ LLGLSLShader::finishProfile(false);
gbps = -1.f;
LL_WARNS("RenderInit") << "GPU benchmark failed: " << e.what() << LL_ENDL;
}
diff --git a/indra/newview/llfloaterperformance.cpp b/indra/newview/llfloaterperformance.cpp
index 97ae97dafc..ba655ab760 100644
--- a/indra/newview/llfloaterperformance.cpp
+++ b/indra/newview/llfloaterperformance.cpp
@@ -115,12 +115,12 @@ BOOL LLFloaterPerformance::postBuild()
mHUDList = mHUDsPanel->getChild<LLNameListCtrl>("hud_list");
mHUDList->setNameListType(LLNameListCtrl::SPECIAL);
mHUDList->setHoverIconName("StopReload_Off");
- mHUDList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachItem, this, _1));
+ mHUDList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachObject, this, _1));
mObjectList = mComplexityPanel->getChild<LLNameListCtrl>("obj_list");
mObjectList->setNameListType(LLNameListCtrl::SPECIAL);
mObjectList->setHoverIconName("StopReload_Off");
- mObjectList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachItem, this, _1));
+ mObjectList->setIconClickedCallback(boost::bind(&LLFloaterPerformance::detachObject, this, _1));
mSettingsPanel->getChild<LLButton>("advanced_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickAdvanced, this));
mSettingsPanel->getChild<LLButton>("defaults_btn")->setCommitCallback(boost::bind(&LLFloaterPerformance::onClickDefaults, this));
@@ -524,9 +524,13 @@ void LLFloaterPerformance::setFPSText()
getChild<LLTextBox>("fps_lbl")->setValue(fps_text);
}
-void LLFloaterPerformance::detachItem(const LLUUID& item_id)
+void LLFloaterPerformance::detachObject(const LLUUID& obj_id)
{
- LLAppearanceMgr::instance().removeItemFromAvatar(item_id);
+ LLViewerObject* obj = gObjectList.findObject(obj_id);
+ if (obj)
+ {
+ LLAppearanceMgr::instance().removeItemFromAvatar(obj->getAttachmentItemID());
+ }
}
void LLFloaterPerformance::onClickAdvanced()
diff --git a/indra/newview/llfloaterperformance.h b/indra/newview/llfloaterperformance.h
index d2f45a9e2e..03fa9e8184 100644
--- a/indra/newview/llfloaterperformance.h
+++ b/indra/newview/llfloaterperformance.h
@@ -46,7 +46,7 @@ public:
void hidePanels();
void showAutoadjustmentsPanel();
- void detachItem(const LLUUID& item_id);
+ void detachObject(const LLUUID& obj_id);
void onAvatarListRightClick(LLUICtrl* ctrl, S32 x, S32 y);
diff --git a/indra/newview/llviewerobject.cpp b/indra/newview/llviewerobject.cpp
index a53c9c3d23..ed2240ef5b 100644
--- a/indra/newview/llviewerobject.cpp
+++ b/indra/newview/llviewerobject.cpp
@@ -3036,24 +3036,33 @@ void LLViewerObject::fetchInventoryFromServer()
delete mInventory;
mInventory = NULL;
- // Results in processTaskInv
- LLMessageSystem* msg = gMessageSystem;
- msg->newMessageFast(_PREHASH_RequestTaskInventory);
- msg->nextBlockFast(_PREHASH_AgentData);
- msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
- msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
- msg->nextBlockFast(_PREHASH_InventoryData);
- msg->addU32Fast(_PREHASH_LocalID, mLocalID);
- msg->sendReliable(mRegionp->getHost());
-
// This will get reset by doInventoryCallback or processTaskInv
mInvRequestState = INVENTORY_REQUEST_PENDING;
+
+ if (mRegionp && !mRegionp->getCapability("RequestTaskInventory").empty())
+ {
+ LLCoros::instance().launch("LLViewerObject::fetchInventoryFromCapCoro()",
+ boost::bind(&LLViewerObject::fetchInventoryFromCapCoro, mID));
+ }
+ else
+ {
+ LL_WARNS() << "Using old task inventory path!" << LL_ENDL;
+ // Results in processTaskInv
+ LLMessageSystem *msg = gMessageSystem;
+ msg->newMessageFast(_PREHASH_RequestTaskInventory);
+ msg->nextBlockFast(_PREHASH_AgentData);
+ msg->addUUIDFast(_PREHASH_AgentID, gAgent.getID());
+ msg->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
+ msg->nextBlockFast(_PREHASH_InventoryData);
+ msg->addU32Fast(_PREHASH_LocalID, mLocalID);
+ msg->sendReliable(mRegionp->getHost());
+ }
}
}
void LLViewerObject::fetchInventoryDelayed(const F64 &time_seconds)
{
- // unless already waiting, drop previous request and shedule an update
+ // unless already waiting, drop previous request and schedule an update
if (mInvRequestState != INVENTORY_REQUEST_WAIT)
{
if (mInvRequestXFerId != 0)
@@ -3084,6 +3093,80 @@ void LLViewerObject::fetchInventoryDelayedCoro(const LLUUID task_inv, const F64
}
}
+//static
+void LLViewerObject::fetchInventoryFromCapCoro(const LLUUID task_inv)
+{
+ LLViewerObject *obj = gObjectList.findObject(task_inv);
+ if (obj)
+ {
+ LLCore::HttpRequest::policy_t httpPolicy(LLCore::HttpRequest::DEFAULT_POLICY_ID);
+ LLCoreHttpUtil::HttpCoroutineAdapter::ptr_t
+ httpAdapter(new LLCoreHttpUtil::HttpCoroutineAdapter("TaskInventoryRequest", httpPolicy));
+ LLCore::HttpRequest::ptr_t httpRequest(new LLCore::HttpRequest);
+ std::string url = obj->mRegionp->getCapability("RequestTaskInventory") + "?task_id=" + obj->mID.asString();
+ // If we already have a copy of the inventory then add it so the server won't re-send something we already have.
+ // We expect this case to crop up in the case of failed inventory mutations, but it might happen otherwise as well.
+ if (obj->mInventorySerialNum && obj->mInventory)
+ url += "&inventory_serial=" + std::to_string(obj->mInventorySerialNum);
+
+ obj->mInvRequestState = INVENTORY_XFER;
+ LLSD result = httpAdapter->getAndSuspend(httpRequest, url);
+
+ LLSD httpResults = result[LLCoreHttpUtil::HttpCoroutineAdapter::HTTP_RESULTS];
+ LLCore::HttpStatus status = LLCoreHttpUtil::HttpCoroutineAdapter::getStatusFromLLSD(httpResults);
+
+ // Object may have gone away while we were suspended, double-check that it still exists
+ obj = gObjectList.findObject(task_inv);
+ if (!obj)
+ {
+ LL_WARNS() << "Object " << task_inv << " went away while fetching inventory, dropping result" << LL_ENDL;
+ return;
+ }
+
+ bool potentially_stale = false;
+ if (status)
+ {
+ // Dealing with inventory serials is kind of funky. They're monotonically increasing and 16 bits,
+ // so we expect them to overflow, but we can use inv serial < expected serial as a signal that we may
+ // have mutated the task inventory since we kicked off the request, and those mutations may have not
+ // been taken into account yet. Of course, those mutations may have actually failed which would result
+ // in the inv serial never increasing.
+ //
+ // When we detect this case, set the expected inv serial to the inventory serial we actually received
+ // and kick off a re-request after a slight delay.
+ S16 serial = (S16)result["inventory_serial"].asInteger();
+ potentially_stale = serial < obj->mExpectedInventorySerialNum;
+ LL_INFOS() << "Inventory loaded for " << task_inv << LL_ENDL;
+ obj->mInventorySerialNum = serial;
+ obj->mExpectedInventorySerialNum = serial;
+ obj->loadTaskInvLLSD(result);
+ }
+ else if (status.getType() == 304)
+ {
+ LL_INFOS() << "Inventory wasn't changed on server!" << LL_ENDL;
+ obj->mInvRequestState = INVENTORY_REQUEST_STOPPED;
+ // Even though it wasn't necessary to send a response, we still may have mutated
+ // the inventory since we kicked off the request, check for that case.
+ potentially_stale = obj->mInventorySerialNum < obj->mExpectedInventorySerialNum;
+ // Set this to what we already have so that we don't re-request a second time.
+ obj->mExpectedInventorySerialNum = obj->mInventorySerialNum;
+ }
+ else
+ {
+ // Not sure that there's anything sensible we can do to recover here, retrying in a loop would be bad.
+ LL_WARNS() << "Error status while requesting task inventory: " << status.toString() << LL_ENDL;
+ obj->mInvRequestState = INVENTORY_REQUEST_STOPPED;
+ }
+
+ if (potentially_stale)
+ {
+ // Stale? I guess we can use what we got for now, but we'll have to re-request
+ LL_WARNS() << "Stale inv_serial? Re-requesting." << LL_ENDL;
+ obj->fetchInventoryDelayed(INVENTORY_UPDATE_WAIT_TIME_OUTDATED);
+ }
+ }
+}
+
LLControlAvatar *LLViewerObject::getControlAvatar()
{
return getRootEdit()->mControlAvatar.get();
@@ -3259,6 +3342,20 @@ void LLViewerObject::processTaskInv(LLMessageSystem* msg, void** user_data)
S16 serial = 0;
msg->getS16Fast(_PREHASH_InventoryData, _PREHASH_Serial, serial);
+ if (object->mRegionp && !object->mRegionp->getCapability("RequestTaskInventory").empty())
+ {
+ // It seems that simulator may ask us to re-download the task inventory if an update to the inventory
+ // happened out-of-band while we had the object selected (like if a script is saved.)
+ //
+ // If we're meant to use the HTTP capability, ignore the contents of the UDP message and fetch the
+ // inventory via the CAP so that we don't flow down the UDP inventory request path unconditionally here.
+ // We shouldn't need to wait, as any updates should already be ready to fetch by this point.
+ LL_INFOS() << "Handling unsolicited ReplyTaskInventory for " << task_id << LL_ENDL;
+ object->mExpectedInventorySerialNum = serial;
+ object->fetchInventoryFromServer();
+ return;
+ }
+
if (serial == object->mInventorySerialNum
&& serial < object->mExpectedInventorySerialNum)
{
@@ -3466,6 +3563,47 @@ BOOL LLViewerObject::loadTaskInvFile(const std::string& filename)
return TRUE;
}
+void LLViewerObject::loadTaskInvLLSD(const LLSD& inv_result)
+{
+ if (inv_result.has("contents"))
+ {
+ if(mInventory)
+ {
+ mInventory->clear(); // will deref and delete it
+ }
+ else
+ {
+ mInventory = new LLInventoryObject::object_list_t;
+ }
+
+ // Synthesize the "Contents" category, the viewer expects it, but it isn't sent.
+ LLPointer<LLInventoryObject> inv = new LLInventoryObject(mID, LLUUID::null, LLAssetType::AT_CATEGORY, "Contents");
+ mInventory->push_front(inv);
+
+ const LLSD& inventory = inv_result["contents"];
+ for (const auto& inv_entry : llsd::inArray(inventory))
+ {
+ if (inv_entry.has("item_id"))
+ {
+ LLPointer<LLViewerInventoryItem> inv = new LLViewerInventoryItem;
+ inv->unpackMessage(inv_entry);
+ mInventory->push_front(inv);
+ }
+ else
+ {
+ LL_WARNS_ONCE() << "Unknown inventory entry while reading from inventory file. Entry: '"
+ << inv_entry << "'" << LL_ENDL;
+ }
+ }
+ }
+ else
+ {
+ LL_WARNS() << "unable to load task inventory: " << inv_result << LL_ENDL;
+ return;
+ }
+ doInventoryCallback();
+}
+
void LLViewerObject::doInventoryCallback()
{
for (callback_list_t::iterator iter = mInventoryCallbacks.begin();
diff --git a/indra/newview/llviewerobject.h b/indra/newview/llviewerobject.h
index fa2bbd0a15..783538fb13 100644
--- a/indra/newview/llviewerobject.h
+++ b/indra/newview/llviewerobject.h
@@ -662,6 +662,7 @@ private:
// forms task inventory request after some time passed, marks request as pending
void fetchInventoryDelayed(const F64 &time_seconds);
static void fetchInventoryDelayedCoro(const LLUUID task_inv, const F64 time_seconds);
+ static void fetchInventoryFromCapCoro(const LLUUID task_inv);
public:
//
@@ -791,6 +792,7 @@ protected:
static void processTaskInvFile(void** user_data, S32 error_code, LLExtStat ext_status);
BOOL loadTaskInvFile(const std::string& filename);
+ void loadTaskInvLLSD(const LLSD &inv_result);
void doInventoryCallback();
BOOL isOnMap();
diff --git a/indra/newview/llviewerregion.cpp b/indra/newview/llviewerregion.cpp
index f98336240c..a0edfe5c81 100755
--- a/indra/newview/llviewerregion.cpp
+++ b/indra/newview/llviewerregion.cpp
@@ -3098,6 +3098,7 @@ void LLViewerRegionImpl::buildCapabilityNames(LLSD& capabilityNames)
capabilityNames.append("FetchInventory2");
capabilityNames.append("FetchInventoryDescendents2");
capabilityNames.append("IncrementCOFVersion");
+ capabilityNames.append("RequestTaskInventory");
AISAPI::getCapNames(capabilityNames);
capabilityNames.append("InterestList");
diff --git a/indra/test/test.cpp b/indra/test/test.cpp
index cbd1077306..cc2c130072 100644
--- a/indra/test/test.cpp
+++ b/indra/test/test.cpp
@@ -36,6 +36,7 @@
#include "linden_common.h"
#include "llerrorcontrol.h"
+#include "llexception.h"
#include "lltut.h"
#include "chained_callback.h"
#include "stringize.h"
@@ -63,11 +64,6 @@
#pragma warning (pop)
#endif
-#include <boost/scoped_ptr.hpp>
-#include <boost/shared_ptr.hpp>
-#include <boost/make_shared.hpp>
-#include <boost/foreach.hpp>
-
#include <fstream>
void wouldHaveCrashed(const std::string& message);
@@ -176,10 +172,6 @@ public:
LLTestCallback(bool verbose_mode, std::ostream *stream,
std::shared_ptr<LLReplayLog> replayer) :
mVerboseMode(verbose_mode),
- mTotalTests(0),
- mPassedTests(0),
- mFailedTests(0),
- mSkippedTests(0),
// By default, capture a shared_ptr to std::cout, with a no-op "deleter"
// so that destroying the shared_ptr makes no attempt to delete std::cout.
mStream(std::shared_ptr<std::ostream>(&std::cout, [](std::ostream*){})),
@@ -215,6 +207,8 @@ public:
virtual void group_started(const std::string& name) {
LL_INFOS("TestRunner")<<"Unit test group_started name=" << name << LL_ENDL;
*mStream << "Unit test group_started name=" << name << std::endl;
+ mGroup = name;
+ mGroupTests = 0;
super::group_started(name);
}
@@ -227,6 +221,7 @@ public:
virtual void test_completed(const tut::test_result& tr)
{
++mTotalTests;
+ ++mGroupTests;
// If this test failed, dump requested log messages BEFORE stating the
// test result.
@@ -314,12 +309,15 @@ public:
super::run_completed();
}
+ std::string mGroup;
+ int mGroupTests{ 0 };
+
protected:
- bool mVerboseMode;
- int mTotalTests;
- int mPassedTests;
- int mFailedTests;
- int mSkippedTests;
+ bool mVerboseMode{ false };
+ int mTotalTests{ 0 };
+ int mPassedTests{ 0 };
+ int mFailedTests{ 0 };
+ int mSkippedTests{ 0 };
std::shared_ptr<std::ostream> mStream;
std::shared_ptr<LLReplayLog> mReplayer;
};
@@ -647,14 +645,47 @@ int main(int argc, char **argv)
// a chained_callback subclass must be linked with previous
mycallback->link();
- if(test_group.empty())
- {
- tut::runner.get().run_tests();
- }
- else
- {
- tut::runner.get().run_tests(test_group);
- }
+ LL::seh::catcher(
+ // __try
+ [test_group]
+ {
+ if(test_group.empty())
+ {
+ tut::runner.get().run_tests();
+ }
+ else
+ {
+ tut::runner.get().run_tests(test_group);
+ }
+ },
+ // __except
+ [mycallback](U32 code, const std::string& /*stacktrace*/)
+ {
+ static std::map<U32, const char*> codes = {
+ { 0xC0000005, "Access Violation" },
+ { 0xC00000FD, "Stack Overflow" },
+ // ... continue filling in as desired
+ };
+
+ auto found{ codes.find(code) };
+ const char* name = ((found == codes.end())? "unknown" : found->second);
+ auto msg{ stringize("test threw ", std::hex, code, " (", name, ")") };
+
+ // Instead of bombing the whole test run, report this as a test
+ // failure. Arguably, catching structured exceptions should be
+ // hacked into TUT itself.
+ mycallback->test_completed(tut::test_result(
+ mycallback->mGroup,
+ mycallback->mGroupTests+1, // test within group
+ "unknown", // test name
+ tut::test_result::ex, // result: exception
+ // we don't have to throw this exception subclass to use it to
+ // populate the test_result struct
+ Windows_SEH_exception(msg)));
+ // we've left the TUT framework -- finish up by hand
+ mycallback->group_completed(mycallback->mGroup);
+ mycallback->run_completed();
+ });
bool success = (mycallback->getFailedTests() == 0);