summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xautobuild.xml14
-rw-r--r--indra/linux_crash_logger/linux_crash_logger.cpp2
-rw-r--r--indra/linux_crash_logger/llcrashloggerlinux.cpp2
-rw-r--r--indra/linux_crash_logger/llcrashloggerlinux.h2
-rw-r--r--indra/llcommon/CMakeLists.txt9
-rw-r--r--indra/llcommon/llapp.h8
-rw-r--r--indra/llcommon/llcoros.cpp19
-rw-r--r--indra/llcommon/lldependencies.cpp3
-rw-r--r--indra/llcommon/lldependencies.h6
-rw-r--r--indra/llcommon/llerror.h87
-rw-r--r--indra/llcommon/lleventcoro.cpp3
-rw-r--r--indra/llcommon/lleventcoro.h6
-rw-r--r--indra/llcommon/llevents.cpp15
-rw-r--r--indra/llcommon/llevents.h46
-rw-r--r--indra/llcommon/llexception.cpp55
-rw-r--r--indra/llcommon/llexception.h85
-rw-r--r--indra/llcommon/llleap.cpp5
-rw-r--r--indra/llcommon/llleap.h6
-rw-r--r--indra/llcommon/llprocess.cpp21
-rw-r--r--indra/llcommon/llprocess.h6
-rw-r--r--indra/llcommon/llthreadsafequeue.cpp13
-rw-r--r--indra/llcommon/llthreadsafequeue.h7
-rw-r--r--indra/llcommon/lluuid.cpp2
-rw-r--r--indra/llcommon/tests/llerror_test.cpp17
-rw-r--r--indra/llcommon/tests/llexception_test.cpp308
-rw-r--r--indra/llcommon/tests/wrapllerrs.h8
-rw-r--r--indra/llcrashlogger/llcrashlogger.h2
-rw-r--r--indra/llimage/llpngwrapper.cpp29
-rw-r--r--indra/llkdu/llimagej2ckdu.cpp117
-rw-r--r--indra/llmessage/llavatarnamecache.cpp11
-rw-r--r--indra/llmessage/llcoproceduremanager.cpp14
-rw-r--r--indra/llmessage/llhttpnode.cpp12
-rw-r--r--indra/llmessage/tests/commtest.h8
-rw-r--r--indra/llmessage/tests/networkio.h5
-rw-r--r--indra/llrender/llimagegl.cpp14
-rw-r--r--indra/llwindow/llappdelegate-objc.h2
-rw-r--r--indra/llwindow/llwindowmacosx-objc.h2
-rw-r--r--indra/mac_crash_logger/llcrashloggermac.cpp2
-rw-r--r--indra/mac_crash_logger/llcrashloggermac.h2
-rw-r--r--indra/mac_crash_logger/mac_crash_logger.cpp2
-rw-r--r--indra/newview/llaccountingcostmanager.cpp10
-rw-r--r--indra/newview/llappcorehttp.cpp7
-rw-r--r--indra/newview/llappdelegate-objc.mm24
-rw-r--r--indra/newview/llappviewer.cpp479
-rw-r--r--indra/newview/llappviewer.h3
-rw-r--r--indra/newview/llappviewerlinux.cpp6
-rw-r--r--indra/newview/llappviewermacosx.cpp9
-rw-r--r--indra/newview/llappviewerwin32.cpp52
-rw-r--r--indra/newview/llcommandlineparser.cpp24
-rw-r--r--indra/newview/llsecapi.cpp13
-rw-r--r--indra/newview/llsecapi.h34
-rw-r--r--indra/newview/llsechandler_basic.cpp90
-rw-r--r--indra/test/llapp_tut.cpp2
-rw-r--r--indra/viewer_components/login/lllogin.cpp102
-rw-r--r--indra/viewer_components/updater/llupdatedownloader.cpp16
-rw-r--r--indra/viewer_components/updater/llupdateinstaller.cpp12
-rw-r--r--indra/viewer_components/updater/llupdaterservice.cpp9
-rw-r--r--indra/viewer_components/updater/llupdaterservice.h5
-rw-r--r--indra/win_crash_logger/llcrashloggerwindows.cpp8
-rw-r--r--indra/win_crash_logger/llcrashloggerwindows.h2
-rw-r--r--indra/win_crash_logger/win_crash_logger.cpp2
61 files changed, 1287 insertions, 599 deletions
diff --git a/autobuild.xml b/autobuild.xml
index b8ed3a4d12..2d8bd22a1c 100755
--- a/autobuild.xml
+++ b/autobuild.xml
@@ -1454,9 +1454,9 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>faa1e5b7cf70c143caabe190fa5588ce</string>
+ <string>fddd634dec5ec03924d62cc774f7f8ea</string>
<key>url</key>
- <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/llappearance_viewer-update-llappearance-utility/rev/304432/arch/Linux/installer/llappearance_utility-0.0.1-linux-304432.tar.bz2</string>
+ <string>http://s3-proxy.lindenlab.com/private-builds-secondlife-com/hg/repo/p64_viewer-llappearance-utility/rev/317266/arch/Linux/installer/llappearance_utility-0.0.1-linux-317266.tar.bz2</string>
</map>
<key>name</key>
<string>linux</string>
@@ -1484,11 +1484,11 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>db992d58c46c80df7d4d31f8a4784b98</string>
+ <string>29a1f64df46094eda0d681821a98d17e</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
- <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llceflib_3p-llceflib/rev/317959/arch/Darwin/installer/llceflib-1.5.3.317959-darwin-317959.tar.bz2</string>
+ <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llceflib_3p-llceflib/rev/311349/arch/Darwin/installer/llceflib-1.5.3.311349-darwin-311349.tar.bz2</string>
</map>
<key>name</key>
<string>darwin</string>
@@ -1498,18 +1498,18 @@
<key>archive</key>
<map>
<key>hash</key>
- <string>bb3818628131a99cd789febfad9dc2c2</string>
+ <string>827b7c339a2cd401d9d23f9ee02cb83f</string>
<key>hash_algorithm</key>
<string>md5</string>
<key>url</key>
- <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llceflib_3p-llceflib/rev/317959/arch/CYGWIN/installer/llceflib-1.5.3.317959-windows-317959.tar.bz2</string>
+ <string>http://automated-builds-secondlife-com.s3.amazonaws.com/hg/repo/3p-llceflib_3p-llceflib/rev/311349/arch/CYGWIN/installer/llceflib-1.5.3.311349-windows-311349.tar.bz2</string>
</map>
<key>name</key>
<string>windows</string>
</map>
</map>
<key>version</key>
- <string>1.5.3.317959</string>
+ <string>1.5.3.311349</string>
</map>
<key>llphysicsextensions_source</key>
<map>
diff --git a/indra/linux_crash_logger/linux_crash_logger.cpp b/indra/linux_crash_logger/linux_crash_logger.cpp
index 9d5ec33fed..63e5409876 100644
--- a/indra/linux_crash_logger/linux_crash_logger.cpp
+++ b/indra/linux_crash_logger/linux_crash_logger.cpp
@@ -51,7 +51,7 @@ int main(int argc, char **argv)
return 1;
}
- app.mainLoop();
+ app.frame();
app.cleanup();
LL_INFOS() << "Crash reporter finished normally." << LL_ENDL;
return 0;
diff --git a/indra/linux_crash_logger/llcrashloggerlinux.cpp b/indra/linux_crash_logger/llcrashloggerlinux.cpp
index e2d2e7ff26..4092d43fc5 100644
--- a/indra/linux_crash_logger/llcrashloggerlinux.cpp
+++ b/indra/linux_crash_logger/llcrashloggerlinux.cpp
@@ -114,7 +114,7 @@ void LLCrashLoggerLinux::gatherPlatformSpecificFiles()
{
}
-bool LLCrashLoggerLinux::mainLoop()
+bool LLCrashLoggerLinux::frame()
{
bool send_logs = true;
if(CRASH_BEHAVIOR_ASK == getCrashBehavior())
diff --git a/indra/linux_crash_logger/llcrashloggerlinux.h b/indra/linux_crash_logger/llcrashloggerlinux.h
index dae6c46651..789f6f03f5 100644
--- a/indra/linux_crash_logger/llcrashloggerlinux.h
+++ b/indra/linux_crash_logger/llcrashloggerlinux.h
@@ -36,7 +36,7 @@ class LLCrashLoggerLinux : public LLCrashLogger
public:
LLCrashLoggerLinux(void);
~LLCrashLoggerLinux(void);
- virtual bool mainLoop();
+ virtual bool frame();
virtual void updateApplication(const std::string& = LLStringUtil::null);
virtual void gatherPlatformSpecificFiles();
virtual bool cleanup();
diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt
index 907dbab8f8..410a5819b3 100644
--- a/indra/llcommon/CMakeLists.txt
+++ b/indra/llcommon/CMakeLists.txt
@@ -58,6 +58,7 @@ set(llcommon_SOURCE_FILES
lleventfilter.cpp
llevents.cpp
lleventtimer.cpp
+ llexception.cpp
llfasttimer.cpp
llfile.cpp
llfindlocale.cpp
@@ -157,6 +158,7 @@ set(llcommon_HEADER_FILES
lleventfilter.h
llevents.h
lleventemitter.h
+ llexception.h
llfasttimer.h
llfile.h
llfindlocale.h
@@ -315,7 +317,7 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llprocinfo "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llrand "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llsdserialize "" "${test_libs}")
- LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
+ LL_ADD_INTEGRATION_TEST(llsingleton "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstring "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltrace "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(lltreeiterators "" "${test_libs}")
@@ -328,6 +330,11 @@ if (LL_TESTS)
LL_ADD_INTEGRATION_TEST(llleap "" "${test_libs}")
LL_ADD_INTEGRATION_TEST(llstreamqueue "" "${test_libs}")
+## llexception_test.cpp isn't a regression test, and doesn't need to be run
+## every build. It's to help a developer make implementation choices about
+## throwing and catching exceptions.
+##LL_ADD_INTEGRATION_TEST(llexception "" "${test_libs}")
+
# *TODO - reenable these once tcmalloc libs no longer break the build.
#ADD_BUILD_TEST(llallocator llcommon)
#ADD_BUILD_TEST(llallocator_heap_profile llcommon)
diff --git a/indra/llcommon/llapp.h b/indra/llcommon/llapp.h
index d9933b3d36..ff9a92b45f 100644
--- a/indra/llcommon/llapp.h
+++ b/indra/llcommon/llapp.h
@@ -172,12 +172,12 @@ public:
virtual bool cleanup() = 0; // Override to do application cleanup
//
- // mainLoop()
+ // frame()
//
- // Runs the application main loop. It's assumed that when you exit
- // this method, the application is in one of the cleanup states, either QUITTING or ERROR
+ // Pass control to the application for a single frame. Returns 'done'
+ // flag: if frame() returns false, it expects to be called again.
//
- virtual bool mainLoop() = 0; // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
+ virtual bool frame() = 0; // Override for application body logic
//
// Crash logging
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 5bbce4325b..8e516d8beb 100644
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -38,6 +38,7 @@
#include "llevents.h"
#include "llerror.h"
#include "stringize.h"
+#include "llexception.h"
// do nothing, when we need nothing done
void LLCoros::no_cleanup(CoroData*) {}
@@ -235,7 +236,23 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla
// capture the 'self' param in CoroData
data->mSelf = &self;
// run the code the caller actually wants in the coroutine
- callable();
+ try
+ {
+ callable();
+ }
+ catch (const LLContinueError&)
+ {
+ // Any uncaught exception derived from LLContinueError will be caught
+ // here and logged. This coroutine will terminate but the rest of the
+ // viewer will carry on.
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName));
+ }
+ catch (...)
+ {
+ // Any OTHER kind of uncaught exception will cause the viewer to
+ // crash, hopefully informatively.
+ CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName));
+ }
// This cleanup isn't perfectly symmetrical with the way we initially set
// data->mPrev, but this is our last chance to reset mCurrentCoro.
sCurrentCoro.reset(data->mPrev);
diff --git a/indra/llcommon/lldependencies.cpp b/indra/llcommon/lldependencies.cpp
index 0e72c175cb..0d5757effd 100644
--- a/indra/llcommon/lldependencies.cpp
+++ b/indra/llcommon/lldependencies.cpp
@@ -40,6 +40,7 @@
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/exception.hpp>
// other Linden headers
+#include "llexception.h"
LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const EdgeList& edges) const
{
@@ -76,7 +77,7 @@ LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const
// Omit independent nodes: display only those that might contribute to
// the cycle.
describe(out, false);
- throw Cycle(out.str());
+ LLTHROW(Cycle(out.str()));
}
// A peculiarity of boost::topological_sort() is that it emits results in
// REVERSE topological order: to get the result you want, you must
diff --git a/indra/llcommon/lldependencies.h b/indra/llcommon/lldependencies.h
index e0294e271b..125bd6a835 100644
--- a/indra/llcommon/lldependencies.h
+++ b/indra/llcommon/lldependencies.h
@@ -34,13 +34,13 @@
#include <vector>
#include <set>
#include <map>
-#include <stdexcept>
#include <iosfwd>
#include <boost/iterator/transform_iterator.hpp>
#include <boost/iterator/indirect_iterator.hpp>
#include <boost/range/iterator_range.hpp>
#include <boost/function.hpp>
#include <boost/bind.hpp>
+#include "llexception.h"
/*****************************************************************************
* Utilities
@@ -106,9 +106,9 @@ public:
/**
* Exception thrown by sort() if there's a cycle
*/
- struct Cycle: public std::runtime_error
+ struct Cycle: public LLException
{
- Cycle(const std::string& what): std::runtime_error(what) {}
+ Cycle(const std::string& what): LLException(what) {}
};
/**
diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h
index 3beef65723..7cbe4334b3 100644
--- a/indra/llcommon/llerror.h
+++ b/indra/llcommon/llerror.h
@@ -174,7 +174,8 @@ namespace LLError
// not really a level
// used to indicate that no messages should be logged
};
-
+ // If you change ELevel, please update llvlog() macro below.
+
/* Macro support
The classes CallSite and Log are used by the logging macros below.
They are not intended for general use.
@@ -305,24 +306,38 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
/////////////////////////////////
// Error Logging Macros
-// See top of file for common usage.
+// See top of file for common usage.
/////////////////////////////////
-// this macro uses a one-shot do statement to avoid parsing errors when writing control flow statements
-// without braces:
-// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL
-
-#define lllog(level, once, ...) \
- do { \
- const char* tags[] = {"", ##__VA_ARGS__}; \
- ::size_t tag_count = LL_ARRAY_SIZE(tags) - 1; \
- static LLError::CallSite _site( \
- level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), __FUNCTION__, once, &tags[1], tag_count);\
- if (LL_UNLIKELY(_site.shouldLog())) \
- { \
- std::ostringstream* _out = LLError::Log::out(); \
+// Instead of using LL_DEBUGS(), LL_INFOS() et al., it may be tempting to
+// directly code the lllog() macro so you can pass in the LLError::ELevel as a
+// variable. DON'T DO IT! The reason is that the first time control passes
+// through lllog(), it initializes a local static LLError::CallSite with that
+// *first* ELevel value. All subsequent visits will decide whether or not to
+// emit output based on the *first* ELevel value bound into that static
+// CallSite instance. Use LL_VLOGS() instead. lllog() assumes its ELevel
+// argument never varies.
+
+// this macro uses a one-shot do statement to avoid parsing errors when
+// writing control flow statements without braces:
+// if (condition) LL_INFOS() << "True" << LL_ENDL; else LL_INFOS()() << "False" << LL_ENDL;
+
+#define lllog(level, once, ...) \
+ do { \
+ const char* tags[] = {"", ##__VA_ARGS__}; \
+ static LLError::CallSite _site(lllog_site_args_(level, once, tags)); \
+ lllog_test_()
+
+#define lllog_test_() \
+ if (LL_UNLIKELY(_site.shouldLog())) \
+ { \
+ std::ostringstream* _out = LLError::Log::out(); \
(*_out)
+#define lllog_site_args_(level, once, tags) \
+ level, __FILE__, __LINE__, typeid(_LL_CLASS_TO_LOG), \
+ __FUNCTION__, once, &tags[1], LL_ARRAY_SIZE(tags)-1
+
//Use this construct if you need to do computation in the middle of a
//message:
//
@@ -363,4 +378,46 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG;
#define LL_INFOS_ONCE(...) lllog(LLError::LEVEL_INFO, true, ##__VA_ARGS__)
#define LL_WARNS_ONCE(...) lllog(LLError::LEVEL_WARN, true, ##__VA_ARGS__)
+// Use this if you need to pass LLError::ELevel as a variable.
+#define LL_VLOGS(level, ...) llvlog(level, false, ##__VA_ARGS__)
+#define LL_VLOGS_ONCE(level, ...) llvlog(level, true, ##__VA_ARGS__)
+
+// The problem with using lllog() with a variable level is that the first time
+// through, it initializes a static CallSite instance with whatever level you
+// pass. That first level is bound into the CallSite; the level parameter is
+// never again examined. One approach to variable level would be to
+// dynamically construct a CallSite instance every call -- which could get
+// expensive, depending on context. So instead, initialize a static CallSite
+// for each level value we support, then dynamically select the CallSite
+// instance for the passed level value.
+// Compare implementation to lllog() above.
+#define llvlog(level, once, ...) \
+ do { \
+ const char* tags[] = {"", ##__VA_ARGS__}; \
+ /* Need a static CallSite instance per expected ELevel value. */ \
+ /* Since we intend to index this array with the ELevel, */ \
+ /* _sites[0] should be ELevel(0), and so on -- avoid using */ \
+ /* ELevel symbolic names when initializing -- except for */ \
+ /* the last entry, which handles anything beyond the end. */ \
+ /* (Commented ELevel value names are from 2016-09-01.) */ \
+ /* Passing an ELevel past the end of this array is itself */ \
+ /* a fatal error, so ensure the last is LEVEL_ERROR. */ \
+ static LLError::CallSite _sites[] = \
+ { \
+ /* LEVEL_DEBUG */ \
+ LLError::CallSite(lllog_site_args_(LLError::ELevel(0), once, tags)), \
+ /* LEVEL_INFO */ \
+ LLError::CallSite(lllog_site_args_(LLError::ELevel(1), once, tags)), \
+ /* LEVEL_WARN */ \
+ LLError::CallSite(lllog_site_args_(LLError::ELevel(2), once, tags)), \
+ /* LEVEL_ERROR */ \
+ LLError::CallSite(lllog_site_args_(LLError::LEVEL_ERROR, once, tags)) \
+ }; \
+ /* Clamp the passed 'level' to at most last entry */ \
+ std::size_t which((std::size_t(level) >= LL_ARRAY_SIZE(_sites)) ? \
+ (LL_ARRAY_SIZE(_sites) - 1) : std::size_t(level)); \
+ /* selected CallSite *must* be named _site for LL_ENDL */ \
+ LLError::CallSite& _site(_sites[which]); \
+ lllog_test_()
+
#endif // LL_LLERROR_H
diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
index 2d5f964deb..56367b8f54 100644
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -39,6 +39,7 @@
#include "llerror.h"
#include "llcoros.h"
#include "llmake.h"
+#include "llexception.h"
#include "lleventfilter.h"
@@ -351,7 +352,7 @@ LLSD errorException(const LLEventWithID& result, const std::string& desc)
// returning it, deliver it via exception.
if (result.second)
{
- throw LLErrorEvent(desc, result.first);
+ LLTHROW(LLErrorEvent(desc, result.first));
}
// That way, our caller knows a simple return must be from the reply
// pump (pump 0).
diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h
index 87926c692d..84827aab4a 100644
--- a/indra/llcommon/lleventcoro.h
+++ b/indra/llcommon/lleventcoro.h
@@ -31,10 +31,10 @@
#include <boost/optional.hpp>
#include <string>
-#include <stdexcept>
#include <utility> // std::pair
#include "llevents.h"
#include "llerror.h"
+#include "llexception.h"
/**
* Like LLListenerOrPumpName, this is a class intended for parameter lists:
@@ -234,11 +234,11 @@ LLSD errorException(const LLEventWithID& result, const std::string& desc);
* because it's not an error in event processing: rather, this exception
* announces an event that bears error information (for some other API).
*/
-class LL_COMMON_API LLErrorEvent: public std::runtime_error
+class LL_COMMON_API LLErrorEvent: public LLException
{
public:
LLErrorEvent(const std::string& what, const LLSD& data):
- std::runtime_error(what),
+ LLException(what),
mData(data)
{}
virtual ~LLErrorEvent() throw() {}
diff --git a/indra/llcommon/llevents.cpp b/indra/llcommon/llevents.cpp
index 645c29d770..19d700a3b0 100644
--- a/indra/llcommon/llevents.cpp
+++ b/indra/llcommon/llevents.cpp
@@ -57,6 +57,7 @@
#include "stringize.h"
#include "llerror.h"
#include "llsdutil.h"
+#include "llexception.h"
#if LL_MSVC
#pragma warning (disable : 4702)
#endif
@@ -174,7 +175,7 @@ std::string LLEventPumps::registerNew(const LLEventPump& pump, const std::string
// Unless we're permitted to tweak it, that's Bad.
if (! tweak)
{
- throw LLEventPump::DupPumpName(std::string("Duplicate LLEventPump name '") + name + "'");
+ LLTHROW(LLEventPump::DupPumpName("Duplicate LLEventPump name '" + name + "'"));
}
// The passed name isn't unique, but we're permitted to tweak it. Find the
// first decimal-integer suffix not already taken. The insert() attempt
@@ -326,8 +327,8 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
// is only when the existing connection object is still connected.
if (found != mConnections.end() && found->second.connected())
{
- throw DupListenerName(std::string("Attempt to register duplicate listener name '") + name +
- "' on " + typeid(*this).name() + " '" + getName() + "'");
+ LLTHROW(DupListenerName("Attempt to register duplicate listener name '" + name +
+ "' on " + typeid(*this).name() + " '" + getName() + "'"));
}
// Okay, name is unique, try to reconcile its dependencies. Specify a new
// "node" value that we never use for an mSignal placement; we'll fix it
@@ -353,8 +354,8 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
// unsortable. If we leave the new node in mDeps, it will continue
// to screw up all future attempts to sort()! Pull it out.
mDeps.remove(name);
- throw Cycle(std::string("New listener '") + name + "' on " + typeid(*this).name() +
- " '" + getName() + "' would cause cycle: " + e.what());
+ LLTHROW(Cycle("New listener '" + name + "' on " + typeid(*this).name() +
+ " '" + getName() + "' would cause cycle: " + e.what()));
}
// Walk the list to verify that we haven't changed the order.
float previous = 0.0, myprev = 0.0;
@@ -418,7 +419,7 @@ LLBoundListener LLEventPump::listen_impl(const std::string& name, const LLEventL
// NOW remove the offending listener node.
mDeps.remove(name);
// Having constructed a description of the order change, inform caller.
- throw OrderChange(out.str());
+ LLTHROW(OrderChange(out.str()));
}
// This node becomes the previous one.
previous = dmi->second;
@@ -608,7 +609,7 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const
{
if (! mListener)
{
- throw Empty("attempting to call uninitialized");
+ LLTHROW(Empty("attempting to call uninitialized"));
}
return (*mListener)(event);
}
diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h
index ba4fcd766e..1526128725 100644
--- a/indra/llcommon/llevents.h
+++ b/indra/llcommon/llevents.h
@@ -37,7 +37,6 @@
#include <set>
#include <vector>
#include <deque>
-#include <stdexcept>
#if LL_WINDOWS
#pragma warning (push)
#pragma warning (disable : 4263) // boost::signals2::expired_slot::what() has const mismatch
@@ -62,6 +61,7 @@
#include "llsingleton.h"
#include "lldependencies.h"
#include "llstl.h"
+#include "llexception.h"
/*==========================================================================*|
// override this to allow binding free functions with more parameters
@@ -95,12 +95,32 @@ struct LLStopWhenHandled
result_type operator()(InputIterator first, InputIterator last) const
{
for (InputIterator si = first; si != last; ++si)
- {
- if (*si)
- {
- return true;
- }
- }
+ {
+ try
+ {
+ if (*si)
+ {
+ return true;
+ }
+ }
+ catch (const LLContinueError&)
+ {
+ // We catch LLContinueError here because an LLContinueError-
+ // based exception means the viewer as a whole should carry on
+ // to the best of our ability. Therefore subsequent listeners
+ // on the same LLEventPump should still receive this event.
+
+ // The iterator passed to a boost::signals2 Combiner is very
+ // clever, but provides no contextual information. We would
+ // very much like to be able to log the name of the LLEventPump
+ // plus the name of this particular listener, but alas.
+ LOG_UNHANDLED_EXCEPTION("LLEventPump");
+ }
+ // We do NOT catch (...) here because we might as well let it
+ // propagate out to the generic handler. If we were able to log
+ // context information here, that would be great, but we can't, so
+ // there's no point.
+ }
return false;
}
};
@@ -188,10 +208,10 @@ public:
bool operator()(const LLSD& event) const;
/// exception if you try to call when empty
- struct Empty: public std::runtime_error
+ struct Empty: public LLException
{
Empty(const std::string& what):
- std::runtime_error(std::string("LLListenerOrPumpName::Empty: ") + what) {}
+ LLException(std::string("LLListenerOrPumpName::Empty: ") + what) {}
};
private:
@@ -371,10 +391,10 @@ public:
* you didn't pass <tt>tweak=true</tt> to permit it to generate a unique
* variant.
*/
- struct DupPumpName: public std::runtime_error
+ struct DupPumpName: public LLException
{
DupPumpName(const std::string& what):
- std::runtime_error(std::string("DupPumpName: ") + what) {}
+ LLException(std::string("DupPumpName: ") + what) {}
};
/**
@@ -399,9 +419,9 @@ public:
/// group exceptions thrown by listen(). We use exceptions because these
/// particular errors are likely to be coding errors, found and fixed by
/// the developer even before preliminary checkin.
- struct ListenError: public std::runtime_error
+ struct ListenError: public LLException
{
- ListenError(const std::string& what): std::runtime_error(what) {}
+ ListenError(const std::string& what): LLException(what) {}
};
/**
* exception thrown by listen(). You are attempting to register a
diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp
new file mode 100644
index 0000000000..b32ec2c9c9
--- /dev/null
+++ b/indra/llcommon/llexception.cpp
@@ -0,0 +1,55 @@
+/**
+ * @file llexception.cpp
+ * @author Nat Goodspeed
+ * @date 2016-08-12
+ * @brief Implementation for llexception.
+ *
+ * $LicenseInfo:firstyear=2016&license=viewerlgpl$
+ * Copyright (c) 2016, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llexception.h"
+// STL headers
+// std headers
+#include <typeinfo>
+// external library headers
+#include <boost/exception/diagnostic_information.hpp>
+// other Linden headers
+#include "llerror.h"
+#include "llerrorcontrol.h"
+
+namespace {
+// used by crash_on_unhandled_exception_() and log_unhandled_exception_()
+void log_unhandled_exception_(LLError::ELevel level,
+ const char* file, int line, const char* pretty_function,
+ const std::string& context)
+{
+ // log same message but allow caller-specified severity level
+ LL_VLOGS(level, "LLException") << LLError::abbreviateFile(file)
+ << "(" << line << "): Unhandled exception caught in " << pretty_function;
+ if (! context.empty())
+ {
+ LL_CONT << ": " << context;
+ }
+ LL_CONT << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL;
+}
+}
+
+void crash_on_unhandled_exception_(const char* file, int line, const char* pretty_function,
+ const std::string& context)
+{
+ // LL_ERRS() terminates and propagates message into crash dump.
+ log_unhandled_exception_(LLError::LEVEL_ERROR, file, line, pretty_function, context);
+}
+
+void log_unhandled_exception_(const char* file, int line, const char* pretty_function,
+ const std::string& context)
+{
+ // Use LL_WARNS() because we seriously do not expect this to happen
+ // routinely, but we DO expect to return from this function.
+ log_unhandled_exception_(LLError::LEVEL_WARN, file, line, pretty_function, context);
+}
diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h
new file mode 100644
index 0000000000..dfcb7c192f
--- /dev/null
+++ b/indra/llcommon/llexception.h
@@ -0,0 +1,85 @@
+/**
+ * @file llexception.h
+ * @author Nat Goodspeed
+ * @date 2016-06-29
+ * @brief Types needed for generic exception handling
+ *
+ * $LicenseInfo:firstyear=2016&license=viewerlgpl$
+ * Copyright (c) 2016, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+#if ! defined(LL_LLEXCEPTION_H)
+#define LL_LLEXCEPTION_H
+
+#include <stdexcept>
+#include <boost/exception/exception.hpp>
+#include <boost/throw_exception.hpp>
+#include <boost/current_function.hpp>
+
+// "Found someone who can comfort me
+// But there are always exceptions..."
+// - Empty Pages, Traffic, from John Barleycorn (1970)
+// https://www.youtube.com/watch?v=dRH0CGVK7ic
+
+/**
+ * LLException is intended as the common base class from which all
+ * viewer-specific exceptions are derived. Rationale for why it's derived from
+ * both std::exception and boost::exception is explained in
+ * tests/llexception_test.cpp.
+ *
+ * boost::current_exception_diagnostic_information() is quite wonderful: if
+ * all we need to do with an exception is log it, in most places we should
+ * catch (...) and log boost::current_exception_diagnostic_information().
+ * See CRASH_ON_UNHANDLED_EXCEPTION() and LOG_UNHANDLED_EXCEPTION() below.
+ *
+ * There may be circumstances in which it would be valuable to distinguish an
+ * exception explicitly thrown by viewer code from an exception thrown by
+ * (say) a third-party library. Catching (const LLException&) supports such
+ * usage. However, most of the value of this base class is in the
+ * diagnostic_information() available via Boost.Exception.
+ */
+struct LLException:
+ public std::runtime_error,
+ public boost::exception
+{
+ LLException(const std::string& what):
+ std::runtime_error(what)
+ {}
+};
+
+/**
+ * The point of LLContinueError is to distinguish exceptions that need not
+ * terminate the whole viewer session. In general, an uncaught exception will
+ * be logged and will crash the viewer. However, though an uncaught exception
+ * derived from LLContinueError will still be logged, the viewer will attempt
+ * to continue processing.
+ */
+struct LLContinueError: public LLException
+{
+ LLContinueError(const std::string& what):
+ LLException(what)
+ {}
+};
+
+/**
+ * Please use LLTHROW() to throw viewer exceptions whenever possible. This
+ * enriches the exception's diagnostic_information() with the source file,
+ * line and containing function of the LLTHROW() macro.
+ */
+// Currently we implement that using BOOST_THROW_EXCEPTION(). Wrap it in
+// LLTHROW() in case we ever want to revisit that implementation decision.
+#define LLTHROW(x) BOOST_THROW_EXCEPTION(x)
+
+/// Call this macro from a catch (...) clause
+#define CRASH_ON_UNHANDLED_EXCEPTION(CONTEXT) \
+ crash_on_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT)
+void crash_on_unhandled_exception_(const char*, int, const char*, const std::string&);
+
+/// Call this from a catch (const LLContinueError&) clause, or from a catch
+/// (...) clause in which you do NOT want the viewer to crash.
+#define LOG_UNHANDLED_EXCEPTION(CONTEXT) \
+ log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION, CONTEXT)
+void log_unhandled_exception_(const char*, int, const char*, const std::string&);
+
+#endif /* ! defined(LL_LLEXCEPTION_H) */
diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp
index 84d2a12f65..c87d2a3e58 100644
--- a/indra/llcommon/llleap.cpp
+++ b/indra/llcommon/llleap.cpp
@@ -33,6 +33,7 @@
#include "lltimer.h"
#include "lluuid.h"
#include "llleaplistener.h"
+#include "llexception.h"
#if LL_MSVC
#pragma warning (disable : 4355) // 'this' used in initializer list: yes, intentionally
@@ -69,7 +70,7 @@ public:
// Rule out empty vector
if (plugin.empty())
{
- throw Error("no plugin command");
+ LLTHROW(Error("no plugin command"));
}
// Don't leave desc empty either, but in this case, if we weren't
@@ -112,7 +113,7 @@ public:
// If that didn't work, no point in keeping this LLLeap object.
if (! mChild)
{
- throw Error(STRINGIZE("failed to run " << mDesc));
+ LLTHROW(Error(STRINGIZE("failed to run " << mDesc)));
}
// Okay, launch apparently worked. Change our mDonePump listener.
diff --git a/indra/llcommon/llleap.h b/indra/llcommon/llleap.h
index e33f25e530..8aac8a64c5 100644
--- a/indra/llcommon/llleap.h
+++ b/indra/llcommon/llleap.h
@@ -13,9 +13,9 @@
#define LL_LLLEAP_H
#include "llinstancetracker.h"
+#include "llexception.h"
#include <string>
#include <vector>
-#include <stdexcept>
/**
* LLSD Event API Plugin class. Because instances are managed by
@@ -67,9 +67,9 @@ public:
* string(s) passed to create() might come from an external source. This
* way the caller can catch LLLeap::Error and try to recover.
*/
- struct Error: public std::runtime_error
+ struct Error: public LLException
{
- Error(const std::string& what): std::runtime_error(what) {}
+ Error(const std::string& what): LLException(what) {}
};
virtual ~LLLeap();
diff --git a/indra/llcommon/llprocess.cpp b/indra/llcommon/llprocess.cpp
index 44f56daf2d..8c321d06b9 100644
--- a/indra/llcommon/llprocess.cpp
+++ b/indra/llcommon/llprocess.cpp
@@ -34,6 +34,7 @@
#include "llapr.h"
#include "apr_signal.h"
#include "llevents.h"
+#include "llexception.h"
#include <boost/foreach.hpp>
#include <boost/bind.hpp>
@@ -472,9 +473,9 @@ private:
*****************************************************************************/
/// Need an exception to avoid constructing an invalid LLProcess object, but
/// internal use only
-struct LLProcessError: public std::runtime_error
+struct LLProcessError: public LLException
{
- LLProcessError(const std::string& msg): std::runtime_error(msg) {}
+ LLProcessError(const std::string& msg): LLException(msg) {}
};
LLProcessPtr LLProcess::create(const LLSDOrParams& params)
@@ -530,8 +531,8 @@ LLProcess::LLProcess(const LLSDOrParams& params):
if (! params.validateBlock(true))
{
- throw LLProcessError(STRINGIZE("not launched: failed parameter validation\n"
- << LLSDNotationStreamer(params)));
+ LLTHROW(LLProcessError(STRINGIZE("not launched: failed parameter validation\n"
+ << LLSDNotationStreamer(params))));
}
mPostend = params.postend;
@@ -596,10 +597,10 @@ LLProcess::LLProcess(const LLSDOrParams& params):
}
else
{
- throw LLProcessError(STRINGIZE("For " << params.executable()
- << ": unsupported FileParam for " << which
- << ": type='" << fparam.type()
- << "', name='" << fparam.name() << "'"));
+ LLTHROW(LLProcessError(STRINGIZE("For " << params.executable()
+ << ": unsupported FileParam for " << which
+ << ": type='" << fparam.type()
+ << "', name='" << fparam.name() << "'")));
}
}
// By default, pass APR_NO_PIPE for unspecified slots.
@@ -678,7 +679,7 @@ LLProcess::LLProcess(const LLSDOrParams& params):
if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr,
gAPRPoolp)))
{
- throw LLProcessError(STRINGIZE(params << " failed"));
+ LLTHROW(LLProcessError(STRINGIZE(params << " failed")));
}
// arrange to call status_callback()
@@ -1063,7 +1064,7 @@ PIPETYPE& LLProcess::getPipe(FILESLOT slot)
PIPETYPE* wp = getPipePtr<PIPETYPE>(error, slot);
if (! wp)
{
- throw NoPipe(error);
+ LLTHROW(NoPipe(error));
}
return *wp;
}
diff --git a/indra/llcommon/llprocess.h b/indra/llcommon/llprocess.h
index 43ccadc412..bfac4567a5 100644
--- a/indra/llcommon/llprocess.h
+++ b/indra/llcommon/llprocess.h
@@ -30,13 +30,13 @@
#include "llinitparam.h"
#include "llsdparam.h"
#include "llwin32headerslean.h"
+#include "llexception.h"
#include "apr_thread_proc.h"
#include <boost/shared_ptr.hpp>
#include <boost/ptr_container/ptr_vector.hpp>
#include <boost/optional.hpp>
#include <boost/noncopyable.hpp>
#include <iosfwd> // std::ostream
-#include <stdexcept>
#if LL_WINDOWS
#include "llwin32headerslean.h" // for HANDLE
@@ -479,9 +479,9 @@ public:
/// Exception thrown by getWritePipe(), getReadPipe() if you didn't ask to
/// create a pipe at the corresponding FILESLOT.
- struct NoPipe: public std::runtime_error
+ struct NoPipe: public LLException
{
- NoPipe(const std::string& what): std::runtime_error(what) {}
+ NoPipe(const std::string& what): LLException(what) {}
};
/**
diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp
index 185f0d63fb..491f920c0f 100644
--- a/indra/llcommon/llthreadsafequeue.cpp
+++ b/indra/llcommon/llthreadsafequeue.cpp
@@ -27,6 +27,7 @@
#include <apr_pools.h>
#include <apr_queue.h>
#include "llthreadsafequeue.h"
+#include "llexception.h"
@@ -41,13 +42,13 @@ LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * po
{
if(mOwnsPool) {
apr_status_t status = apr_pool_create(&mPool, 0);
- if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate pool");
+ if(status != APR_SUCCESS) LLTHROW(LLThreadSafeQueueError("failed to allocate pool"));
} else {
; // No op.
}
apr_status_t status = apr_queue_create(&mQueue, capacity, mPool);
- if(status != APR_SUCCESS) throw LLThreadSafeQueueError("failed to allocate queue");
+ if(status != APR_SUCCESS) LLTHROW(LLThreadSafeQueueError("failed to allocate queue"));
}
@@ -68,9 +69,9 @@ void LLThreadSafeQueueImplementation::pushFront(void * element)
apr_status_t status = apr_queue_push(mQueue, element);
if(status == APR_EINTR) {
- throw LLThreadSafeQueueInterrupt();
+ LLTHROW(LLThreadSafeQueueInterrupt());
} else if(status != APR_SUCCESS) {
- throw LLThreadSafeQueueError("push failed");
+ LLTHROW(LLThreadSafeQueueError("push failed"));
} else {
; // Success.
}
@@ -88,9 +89,9 @@ void * LLThreadSafeQueueImplementation::popBack(void)
apr_status_t status = apr_queue_pop(mQueue, &element);
if(status == APR_EINTR) {
- throw LLThreadSafeQueueInterrupt();
+ LLTHROW(LLThreadSafeQueueInterrupt());
} else if(status != APR_SUCCESS) {
- throw LLThreadSafeQueueError("pop failed");
+ LLTHROW(LLThreadSafeQueueError("pop failed"));
} else {
return element;
}
diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h
index 58cac38769..45289ef0b4 100644
--- a/indra/llcommon/llthreadsafequeue.h
+++ b/indra/llcommon/llthreadsafequeue.h
@@ -27,9 +27,8 @@
#ifndef LL_LLTHREADSAFEQUEUE_H
#define LL_LLTHREADSAFEQUEUE_H
-
+#include "llexception.h"
#include <string>
-#include <stdexcept>
struct apr_pool_t; // From apr_pools.h
@@ -40,11 +39,11 @@ class LLThreadSafeQueueImplementation; // See below.
// A general queue exception.
//
class LL_COMMON_API LLThreadSafeQueueError:
-public std::runtime_error
+ public LLException
{
public:
LLThreadSafeQueueError(std::string const & message):
- std::runtime_error(message)
+ LLException(message)
{
; // No op.
}
diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp
index e3671047b4..d4af2c6b01 100644
--- a/indra/llcommon/lluuid.cpp
+++ b/indra/llcommon/lluuid.cpp
@@ -83,7 +83,7 @@ unsigned int decode( char const * fiveChars ) throw( bad_input_data )
unsigned int ret = 0;
for( int ix = 0; ix < 5; ++ix ) {
char * s = strchr( encodeTable, fiveChars[ ix ] );
-if( s == 0 ) throw bad_input_data();
+if( s == 0 ) LLTHROW(bad_input_data());
ret = ret * 85 + (s-encodeTable);
}
return ret;
diff --git a/indra/llcommon/tests/llerror_test.cpp b/indra/llcommon/tests/llerror_test.cpp
index f51279e817..8bace8ac41 100644
--- a/indra/llcommon/tests/llerror_test.cpp
+++ b/indra/llcommon/tests/llerror_test.cpp
@@ -237,8 +237,21 @@ namespace tut
void ErrorTestObject::test<4>()
// file abbreviation
{
- std::string thisFile = __FILE__;
- std::string abbreviateFile = LLError::abbreviateFile(thisFile);
+ std::string prev, abbreviateFile = __FILE__;
+ do
+ {
+ prev = abbreviateFile;
+ abbreviateFile = LLError::abbreviateFile(abbreviateFile);
+ // __FILE__ is assumed to end with
+ // indra/llcommon/tests/llerror_test.cpp. This test used to call
+ // abbreviateFile() exactly once, then check below whether it
+ // still contained the string 'indra'. That fails if the FIRST
+ // part of the pathname also contains indra! Certain developer
+ // machine images put local directory trees under
+ // /ngi-persist/indra, which is where we observe the problem. So
+ // now, keep calling abbreviateFile() until it returns its
+ // argument unchanged, THEN check.
+ } while (abbreviateFile != prev);
ensure_ends_with("file name abbreviation",
abbreviateFile,
diff --git a/indra/llcommon/tests/llexception_test.cpp b/indra/llcommon/tests/llexception_test.cpp
new file mode 100644
index 0000000000..6bee1943c2
--- /dev/null
+++ b/indra/llcommon/tests/llexception_test.cpp
@@ -0,0 +1,308 @@
+/**
+ * @file llexception_test.cpp
+ * @author Nat Goodspeed
+ * @date 2016-08-12
+ * @brief Tests for throwing exceptions.
+ *
+ * This isn't a regression test: it doesn't need to be run every build, which
+ * is why the corresponding line in llcommon/CMakeLists.txt is commented out.
+ * Rather it's a head-to-head test of what kind of exception information we
+ * can collect from various combinations of exception base classes, type of
+ * throw verb and sequences of catch clauses.
+ *
+ * This "test" makes no ensure() calls: its output goes to stdout for human
+ * examination.
+ *
+ * As of 2016-08-12 with Boost 1.57, we come to the following conclusions.
+ * These should probably be re-examined from time to time as we update Boost.
+ *
+ * - It is indisputably beneficial to use BOOST_THROW_EXCEPTION() rather than
+ * plain throw. The macro annotates the exception object with the filename,
+ * line number and function name from which the exception was thrown.
+ *
+ * - That being the case, deriving only from boost::exception isn't an option.
+ * Every exception object passed to BOOST_THROW_EXCEPTION() must be derived
+ * directly or indirectly from std::exception. The only question is whether
+ * to also derive from boost::exception. We decided to derive LLException
+ * from both, as it makes message output slightly cleaner, but this is a
+ * trivial reason: if a strong reason emerges to prefer single inheritance,
+ * dropping the boost::exception base class shouldn't be a problem.
+ *
+ * - (As you will have guessed, ridiculous things like a char* or int or a
+ * class derived from neither boost::exception nor std::exception can only
+ * be caught by that specific type or (...), and
+ * boost::current_exception_diagnostic_information() simply throws up its
+ * hands and confesses utter ignorance. Stay away from such nonsense.)
+ *
+ * - But if you derive from std::exception, to nat's surprise,
+ * boost::current_exception_diagnostic_information() gives as much
+ * information about exceptions in a catch (...) clause as you can get from
+ * a specific catch (const std::exception&) clause, notably the concrete
+ * exception class and the what() string. So instead of a sequence like
+ *
+ * try { ... }
+ * catch (const boost::exception& e) { ... boost-flavored logging ... }
+ * catch (const std::exception& e) { ... std::exception logging ... }
+ * catch (...) { ... generic logging ... }
+ *
+ * we should be able to get away with only a catch (...) clause that logs
+ * boost::current_exception_diagnostic_information().
+ *
+ * - Going further: boost::current_exception_diagnostic_information() provides
+ * just as much information even within a std::set_terminate() handler. So
+ * it might not even be strictly necessary to include a catch (...) clause
+ * since the viewer does use std::set_terminate().
+ *
+ * - (We might consider adding a catch (int) clause because Kakadu internally
+ * throws ints, and who knows if one of those might leak out. If it does,
+ * boost::current_exception_diagnostic_information() can do nothing with it.
+ * A catch (int) clause could at least log the value and rethrow.)
+ *
+ * $LicenseInfo:firstyear=2016&license=viewerlgpl$
+ * Copyright (c) 2016, Linden Research, Inc.
+ * $/LicenseInfo$
+ */
+
+// Precompiled header
+#include "linden_common.h"
+// associated header
+#include "llexception.h"
+// STL headers
+// std headers
+#include <typeinfo>
+// external library headers
+#include <boost/throw_exception.hpp>
+// other Linden headers
+#include "../test/lltut.h"
+
+// helper for display output
+// usage: std::cout << center(some string value, fill char, width) << std::endl;
+// (assumes it's the only thing on that particular line)
+struct center
+{
+ center(const std::string& label, char fill, std::size_t width):
+ mLabel(label),
+ mFill(fill),
+ mWidth(width)
+ {}
+
+ // Use friend declaration not because we need to grant access, but because
+ // it lets us declare a free operator like a member function.
+ friend std::ostream& operator<<(std::ostream& out, const center& ctr)
+ {
+ std::size_t padded = ctr.mLabel.length() + 2;
+ std::size_t left = (ctr.mWidth - padded) / 2;
+ std::size_t right = ctr.mWidth - left - padded;
+ return out << std::string(left, ctr.mFill) << ' ' << ctr.mLabel << ' '
+ << std::string(right, ctr.mFill);
+ }
+
+ std::string mLabel;
+ char mFill;
+ std::size_t mWidth;
+};
+
+/*****************************************************************************
+* Four kinds of exceptions: derived from boost::exception, from
+* std::exception, from both, from neither
+*****************************************************************************/
+// Interestingly, we can't use this variant with BOOST_THROW_EXCEPTION()
+// (which we want) -- we reach a failure topped by this comment:
+// //All boost exceptions are required to derive from std::exception,
+// //to ensure compatibility with BOOST_NO_EXCEPTIONS.
+struct FromBoost: public boost::exception
+{
+ FromBoost(const std::string& what): mWhat(what) {}
+ ~FromBoost() throw() {}
+ std::string what() const { return mWhat; }
+ std::string mWhat;
+};
+
+struct FromStd: public std::runtime_error
+{
+ FromStd(const std::string& what): std::runtime_error(what) {}
+};
+
+struct FromBoth: public boost::exception, public std::runtime_error
+{
+ FromBoth(const std::string& what): std::runtime_error(what) {}
+};
+
+// Same deal with FromNeither: can't use with BOOST_THROW_EXCEPTION().
+struct FromNeither
+{
+ FromNeither(const std::string& what): mWhat(what) {}
+ std::string what() const { return mWhat; }
+ std::string mWhat;
+};
+
+/*****************************************************************************
+* Two kinds of throws: plain throw and BOOST_THROW_EXCEPTION()
+*****************************************************************************/
+template <typename EXC>
+void plain_throw(const std::string& what)
+{
+ throw EXC(what);
+}
+
+template <typename EXC>
+void boost_throw(const std::string& what)
+{
+ BOOST_THROW_EXCEPTION(EXC(what));
+}
+
+// Okay, for completeness, functions that throw non-class values. We wouldn't
+// even deign to consider these if we hadn't found examples in our own source
+// code! (Note that Kakadu's internal exception support is still based on
+// throwing ints.)
+void throw_char_ptr(const std::string& what)
+{
+ throw what.c_str(); // umm...
+}
+
+void throw_int(const std::string& what)
+{
+ throw int(what.length());
+}
+
+/*****************************************************************************
+* Three sequences of catch clauses:
+* boost::exception then ...,
+* std::exception then ...,
+* or just ...
+*****************************************************************************/
+void catch_boost_dotdotdot(void (*thrower)(const std::string&), const std::string& what)
+{
+ try
+ {
+ thrower(what);
+ }
+ catch (const boost::exception& e)
+ {
+ std::cout << "catch (const boost::exception& e)" << std::endl;
+ std::cout << "e is " << typeid(e).name() << std::endl;
+ std::cout << "boost::diagnostic_information(e):\n'"
+ << boost::diagnostic_information(e) << "'" << std::endl;
+ // no way to report e.what()
+ }
+ catch (...)
+ {
+ std::cout << "catch (...)" << std::endl;
+ std::cout << "boost::current_exception_diagnostic_information():\n'"
+ << boost::current_exception_diagnostic_information() << "'"
+ << std::endl;
+ }
+}
+
+void catch_std_dotdotdot(void (*thrower)(const std::string&), const std::string& what)
+{
+ try
+ {
+ thrower(what);
+ }
+ catch (const std::exception& e)
+ {
+ std::cout << "catch (const std::exception& e)" << std::endl;
+ std::cout << "e is " << typeid(e).name() << std::endl;
+ std::cout << "boost::diagnostic_information(e):\n'"
+ << boost::diagnostic_information(e) << "'" << std::endl;
+ std::cout << "e.what: '"
+ << e.what() << "'" << std::endl;
+ }
+ catch (...)
+ {
+ std::cout << "catch (...)" << std::endl;
+ std::cout << "boost::current_exception_diagnostic_information():\n'"
+ << boost::current_exception_diagnostic_information() << "'"
+ << std::endl;
+ }
+}
+
+void catch_dotdotdot(void (*thrower)(const std::string&), const std::string& what)
+{
+ try
+ {
+ thrower(what);
+ }
+ catch (...)
+ {
+ std::cout << "catch (...)" << std::endl;
+ std::cout << "boost::current_exception_diagnostic_information():\n'"
+ << boost::current_exception_diagnostic_information() << "'"
+ << std::endl;
+ }
+}
+
+/*****************************************************************************
+* Try a particular kind of throw against each of three catch sequences
+*****************************************************************************/
+void catch_several(void (*thrower)(const std::string&), const std::string& what)
+{
+ std::cout << std::string(20, '-') << "catch_boost_dotdotdot(" << what << ")" << std::endl;
+ catch_boost_dotdotdot(thrower, "catch_boost_dotdotdot(" + what + ")");
+
+ std::cout << std::string(20, '-') << "catch_std_dotdotdot(" << what << ")" << std::endl;
+ catch_std_dotdotdot(thrower, "catch_std_dotdotdot(" + what + ")");
+
+ std::cout << std::string(20, '-') << "catch_dotdotdot(" << what << ")" << std::endl;
+ catch_dotdotdot(thrower, "catch_dotdotdot(" + what + ")");
+}
+
+/*****************************************************************************
+* For a particular kind of exception, try both kinds of throw against all
+* three catch sequences
+*****************************************************************************/
+template <typename EXC>
+void catch_both_several(const std::string& what)
+{
+ std::cout << std::string(20, '*') << "plain_throw<" << what << ">" << std::endl;
+ catch_several(plain_throw<EXC>, "plain_throw<" + what + ">");
+
+ std::cout << std::string(20, '*') << "boost_throw<" << what << ">" << std::endl;
+ catch_several(boost_throw<EXC>, "boost_throw<" + what + ">");
+}
+
+/*****************************************************************************
+* TUT
+*****************************************************************************/
+namespace tut
+{
+ struct llexception_data
+ {
+ };
+ typedef test_group<llexception_data> llexception_group;
+ typedef llexception_group::object object;
+ llexception_group llexceptiongrp("llexception");
+
+ template<> template<>
+ void object::test<1>()
+ {
+ set_test_name("throwing exceptions");
+
+ // For each kind of exception, try both kinds of throw against all
+ // three catch sequences
+ std::size_t margin = 72;
+ std::cout << center("FromStd", '=', margin) << std::endl;
+ catch_both_several<FromStd>("FromStd");
+
+ std::cout << center("FromBoth", '=', margin) << std::endl;
+ catch_both_several<FromBoth>("FromBoth");
+
+ std::cout << center("FromBoost", '=', margin) << std::endl;
+ // can't throw with BOOST_THROW_EXCEPTION(), just use catch_several()
+ catch_several(plain_throw<FromBoost>, "plain_throw<FromBoost>");
+
+ std::cout << center("FromNeither", '=', margin) << std::endl;
+ // can't throw this with BOOST_THROW_EXCEPTION() either
+ catch_several(plain_throw<FromNeither>, "plain_throw<FromNeither>");
+
+ std::cout << center("const char*", '=', margin) << std::endl;
+ // We don't expect BOOST_THROW_EXCEPTION() to throw anything so daft
+ // as a const char* or an int, so don't bother with
+ // catch_both_several() -- just catch_several().
+ catch_several(throw_char_ptr, "throw_char_ptr");
+
+ std::cout << center("int", '=', margin) << std::endl;
+ catch_several(throw_int, "throw_int");
+ }
+} // namespace tut
diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h
index 785197ba11..9a4bbbd630 100644
--- a/indra/llcommon/tests/wrapllerrs.h
+++ b/indra/llcommon/tests/wrapllerrs.h
@@ -35,13 +35,13 @@
#include <tut/tut.hpp>
#include "llerrorcontrol.h"
+#include "llexception.h"
#include "stringize.h"
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <list>
#include <string>
-#include <stdexcept>
// statically reference the function in test.cpp... it's short, we could
// replicate, but better to reuse
@@ -67,9 +67,9 @@ struct WrapLLErrs
LLError::restoreSettings(mPriorErrorSettings);
}
- struct FatalException: public std::runtime_error
+ struct FatalException: public LLException
{
- FatalException(const std::string& what): std::runtime_error(what) {}
+ FatalException(const std::string& what): LLException(what) {}
};
void operator()(const std::string& message)
@@ -78,7 +78,7 @@ struct WrapLLErrs
error = message;
// Also throw an appropriate exception since calling code is likely to
// assume that control won't continue beyond LL_ERRS.
- throw FatalException(message);
+ LLTHROW(FatalException(message));
}
std::string error;
diff --git a/indra/llcrashlogger/llcrashlogger.h b/indra/llcrashlogger/llcrashlogger.h
index 8b4afae24a..56e26c23ba 100644
--- a/indra/llcrashlogger/llcrashlogger.h
+++ b/indra/llcrashlogger/llcrashlogger.h
@@ -57,7 +57,7 @@ public:
LLSD constructPostData();
virtual void updateApplication(const std::string& message = LLStringUtil::null);
virtual bool init();
- virtual bool mainLoop() = 0;
+ virtual bool frame() = 0;
virtual bool cleanup() = 0;
void commonCleanup();
void setUserText(const std::string& text) { mCrashInfo["UserNotes"] = text; }
diff --git a/indra/llimage/llpngwrapper.cpp b/indra/llimage/llpngwrapper.cpp
index aad139f570..da289ea889 100644
--- a/indra/llimage/llpngwrapper.cpp
+++ b/indra/llimage/llpngwrapper.cpp
@@ -31,6 +31,16 @@
#include "llimage.h"
#include "llpngwrapper.h"
+#include "llexception.h"
+
+namespace {
+// Failure to load an image shouldn't crash the whole viewer.
+struct PngError: public LLContinueError
+{
+ PngError(png_const_charp msg): LLContinueError(msg) {}
+};
+} // anonymous namespace
+
// ---------------------------------------------------------------------------
// LLPngWrapper
// ---------------------------------------------------------------------------
@@ -75,11 +85,10 @@ BOOL LLPngWrapper::isValidPng(U8* src)
}
// Called by the libpng library when a fatal encoding or decoding error
-// occurs. We simply throw the error message and let our try/catch
-// block clean up.
+// occurs. We throw PngError and let our try/catch block clean up.
void LLPngWrapper::errorHandler(png_structp png_ptr, png_const_charp msg)
{
- throw msg;
+ LLTHROW(PngError(msg));
}
// Called by the libpng library when reading (decoding) the PNG file. We
@@ -129,7 +138,7 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf
this, &errorHandler, NULL);
if (mReadPngPtr == NULL)
{
- throw "Problem creating png read structure";
+ LLTHROW(PngError("Problem creating png read structure"));
}
// Allocate/initialize the memory for image information.
@@ -187,9 +196,9 @@ BOOL LLPngWrapper::readPng(U8* src, S32 dataSize, LLImageRaw* rawImage, ImageInf
mFinalSize = dataPtr.mOffset;
}
- catch (png_const_charp msg)
+ catch (const PngError& msg)
{
- mErrorMessage = msg;
+ mErrorMessage = msg.what();
releaseResources();
return (FALSE);
}
@@ -288,14 +297,14 @@ BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest)
if (mColorType == -1)
{
- throw "Unsupported image: unexpected number of channels";
+ LLTHROW(PngError("Unsupported image: unexpected number of channels"));
}
mWritePngPtr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
NULL, &errorHandler, NULL);
if (!mWritePngPtr)
{
- throw "Problem creating png write structure";
+ LLTHROW(PngError("Problem creating png write structure"));
}
mWriteInfoPtr = png_create_info_struct(mWritePngPtr);
@@ -339,9 +348,9 @@ BOOL LLPngWrapper::writePng(const LLImageRaw* rawImage, U8* dest)
png_write_end(mWritePngPtr, mWriteInfoPtr);
mFinalSize = dataPtr.mOffset;
}
- catch (png_const_charp msg)
+ catch (const PngError& msg)
{
- mErrorMessage = msg;
+ mErrorMessage = msg.what();
releaseResources();
return (FALSE);
}
diff --git a/indra/llkdu/llimagej2ckdu.cpp b/indra/llkdu/llimagej2ckdu.cpp
index 9347e51b85..9e1dfc7213 100644
--- a/indra/llkdu/llimagej2ckdu.cpp
+++ b/indra/llkdu/llimagej2ckdu.cpp
@@ -1,4 +1,4 @@
- /**
+/**
* @file llimagej2ckdu.cpp
* @brief This is an implementation of JPEG2000 encode/decode using Kakadu
*
@@ -34,6 +34,53 @@
#include "kdu_block_coding.h"
+#include "llexception.h"
+#include <boost/exception/diagnostic_information.hpp>
+#include <sstream>
+#include <iomanip>
+
+namespace {
+// Failure to load an image shouldn't crash the whole viewer.
+struct KDUError: public LLContinueError
+{
+ KDUError(const std::string& msg): LLContinueError(msg) {}
+};
+
+// KDU defines int error codes as hex values, so we should log them in hex
+// so we can grep KDU headers for the hex. However those hex values
+// generally "happen" to encode big-endian multibyte character sequences,
+// e.g. KDU_ERROR_EXCEPTION is 0x6b647545: 'kduE'
+// But beware because KDU_NULL_EXCEPTION is simply 0 -- which doesn't
+// preclude somebody from throwing it.
+std::string report_kdu_exception(kdu_exception mb)
+{
+ std::ostringstream out;
+ // always report mb in hex
+ out << "kdu_exception " << std::hex << mb;
+
+ // Also display as many chars as are encoded in the kdu_exception
+ // value. Make a char array; reserve 1 extra byte for nul terminator.
+ char bytes[sizeof(kdu_exception) + 1];
+ // Back up through 'bytes'
+ char *bptr = bytes + sizeof(bytes);
+ *(--bptr) = '\0';
+ while (mb)
+ {
+ // store low-order byte of mb in next-left char
+ *(--bptr) = char(mb & 0xFF);
+ // then shift mb right by one byte
+ mb >>= 8;
+ }
+ // did that produce any characters?
+ if (*bptr)
+ {
+ out << " (" << bptr << ')';
+ }
+
+ return out.str();
+}
+} // anonymous namespace
+
class kdc_flow_control {
public:
@@ -167,7 +214,7 @@ struct LLKDUMessageError : public LLKDUMessage
// exception if we want to recover from a KDU error.
if (end_of_message)
{
- throw "KDU throwing an exception";
+ LLTHROW(KDUError("LLKDUMessageError::flush()"));
}
}
};
@@ -384,14 +431,24 @@ bool LLImageJ2CKDU::initDecode(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
mTPosp->x = 0;
}
}
- catch (const char* msg)
+ catch (const KDUError& msg)
+ {
+ base.setLastError(msg.what());
+ return false;
+ }
+ catch (kdu_exception kdu_value)
{
- base.setLastError(ll_safe_string(msg));
+ // KDU internally throws kdu_exception. It's possible that such an
+ // exception might leak out into our code. Catch kdu_exception
+ // specially because boost::current_exception_diagnostic_information()
+ // could do nothing with it.
+ base.setLastError(report_kdu_exception(kdu_value));
return false;
}
catch (...)
{
- base.setLastError("Unknown J2C error");
+ base.setLastError("Unknown J2C error: " +
+ boost::current_exception_diagnostic_information());
return false;
}
@@ -480,16 +537,28 @@ bool LLImageJ2CKDU::decodeImpl(LLImageJ2C &base, LLImageRaw &raw_image, F32 deco
return false;
}
}
- catch (const char* msg)
+ catch (const KDUError& msg)
+ {
+ base.setLastError(msg.what());
+ base.decodeFailed();
+ cleanupCodeStream();
+ return true; // done
+ }
+ catch (kdu_exception kdu_value)
{
- base.setLastError(ll_safe_string(msg));
+ // KDU internally throws kdu_exception. It's possible that such an
+ // exception might leak out into our code. Catch kdu_exception
+ // specially because boost::current_exception_diagnostic_information()
+ // could do nothing with it.
+ base.setLastError(report_kdu_exception(kdu_value));
base.decodeFailed();
cleanupCodeStream();
return true; // done
}
catch (...)
{
- base.setLastError( "Unknown J2C error" );
+ base.setLastError("Unknown J2C error: " +
+ boost::current_exception_diagnostic_information());
base.decodeFailed();
cleanupCodeStream();
return true; // done
@@ -673,14 +742,24 @@ bool LLImageJ2CKDU::encodeImpl(LLImageJ2C &base, const LLImageRaw &raw_image, co
base.updateData(); // set width, height
delete[] output_buffer;
}
- catch(const char* msg)
+ catch(const KDUError& msg)
{
- base.setLastError(ll_safe_string(msg));
+ base.setLastError(msg.what());
+ return false;
+ }
+ catch (kdu_exception kdu_value)
+ {
+ // KDU internally throws kdu_exception. It's possible that such an
+ // exception might leak out into our code. Catch kdu_exception
+ // specially because boost::current_exception_diagnostic_information()
+ // could do nothing with it.
+ base.setLastError(report_kdu_exception(kdu_value));
return false;
}
catch( ... )
{
- base.setLastError( "Unknown J2C error" );
+ base.setLastError("Unknown J2C error: " +
+ boost::current_exception_diagnostic_information());
return false;
}
@@ -697,14 +776,24 @@ bool LLImageJ2CKDU::getMetadata(LLImageJ2C &base)
setupCodeStream(base, false, MODE_FAST);
return true;
}
- catch (const char* msg)
+ catch (const KDUError& msg)
+ {
+ base.setLastError(msg.what());
+ return false;
+ }
+ catch (kdu_exception kdu_value)
{
- base.setLastError(ll_safe_string(msg));
+ // KDU internally throws kdu_exception. It's possible that such an
+ // exception might leak out into our code. Catch kdu_exception
+ // specially because boost::current_exception_diagnostic_information()
+ // could do nothing with it.
+ base.setLastError(report_kdu_exception(kdu_value));
return false;
}
catch (...)
{
- base.setLastError( "Unknown J2C error" );
+ base.setLastError("Unknown J2C error: " +
+ boost::current_exception_diagnostic_information());
return false;
}
}
diff --git a/indra/llmessage/llavatarnamecache.cpp b/indra/llmessage/llavatarnamecache.cpp
index 1ca5f58ae2..004db546b7 100644
--- a/indra/llmessage/llavatarnamecache.cpp
+++ b/indra/llmessage/llavatarnamecache.cpp
@@ -43,6 +43,8 @@
#include "llcoros.h"
#include "lleventcoro.h"
#include "llcorehttputil.h"
+#include "llexception.h"
+#include "stringize.h"
#include <map>
#include <set>
@@ -231,13 +233,12 @@ void LLAvatarNameCache::requestAvatarNameCache_(std::string url, std::vector<LLU
LLAvatarNameCache::handleAvNameCacheSuccess(results, httpResults);
}
- catch (std::exception e)
- {
- LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL;
- }
catch (...)
{
- LL_WARNS() << "Caught unknown exception." << LL_ENDL;
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName()
+ << "('" << url << "', " << agentIds.size()
+ << " Agent Ids)"));
+ throw;
}
}
diff --git a/indra/llmessage/llcoproceduremanager.cpp b/indra/llmessage/llcoproceduremanager.cpp
index d4c0788b7d..74cdff2b00 100644
--- a/indra/llmessage/llcoproceduremanager.cpp
+++ b/indra/llmessage/llcoproceduremanager.cpp
@@ -27,6 +27,8 @@
#include "linden_common.h"
#include "llcoproceduremanager.h"
+#include "llexception.h"
+#include "stringize.h"
#include <boost/assign.hpp>
//=========================================================================
@@ -388,14 +390,14 @@ void LLCoprocedurePool::coprocedureInvokerCoro(LLCoreHttpUtil::HttpCoroutineAdap
{
coproc->mProc(httpAdapter, coproc->mId);
}
- catch (std::exception &e)
- {
- LL_WARNS() << "Coprocedure(" << coproc->mName << ") id=" << coproc->mId.asString() <<
- " threw an exception! Message=\"" << e.what() << "\"" << LL_ENDL;
- }
catch (...)
{
- LL_WARNS() << "A non std::exception was thrown from " << coproc->mName << " with id=" << coproc->mId << "." << " in pool \"" << mPoolName << "\"" << LL_ENDL;
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("Coprocedure('" << coproc->mName
+ << "', id=" << coproc->mId.asString()
+ << ") in pool '" << mPoolName << "'"));
+ // must NOT omit this or we deplete the pool
+ mActiveCoprocs.erase(itActive);
+ throw;
}
LL_INFOS() << "Finished coprocedure(" << coproc->mName << ")" << " in pool \"" << mPoolName << "\"" << LL_ENDL;
diff --git a/indra/llmessage/llhttpnode.cpp b/indra/llmessage/llhttpnode.cpp
index f235965879..04b34a296c 100644
--- a/indra/llmessage/llhttpnode.cpp
+++ b/indra/llmessage/llhttpnode.cpp
@@ -31,6 +31,7 @@
#include "llstl.h"
#include "llhttpconstants.h"
+#include "llexception.h"
const std::string CONTEXT_HEADERS("headers");
const std::string CONTEXT_PATH("path");
@@ -92,27 +93,28 @@ LLHTTPNode::~LLHTTPNode()
namespace {
- class NotImplemented
+ struct NotImplemented: public LLException
{
+ NotImplemented(): LLException("LLHTTPNode::NotImplemented") {}
};
}
// virtual
LLSD LLHTTPNode::simpleGet() const
{
- throw NotImplemented();
+ LLTHROW(NotImplemented());
}
// virtual
LLSD LLHTTPNode::simplePut(const LLSD& input) const
{
- throw NotImplemented();
+ LLTHROW(NotImplemented());
}
// virtual
LLSD LLHTTPNode::simplePost(const LLSD& input) const
{
- throw NotImplemented();
+ LLTHROW(NotImplemented());
}
@@ -172,7 +174,7 @@ void LLHTTPNode::del(LLHTTPNode::ResponsePtr response, const LLSD& context) cons
// virtual
LLSD LLHTTPNode::simpleDel(const LLSD&) const
{
- throw NotImplemented();
+ LLTHROW(NotImplemented());
}
// virtual
diff --git a/indra/llmessage/tests/commtest.h b/indra/llmessage/tests/commtest.h
index 0d149b5258..7c8f27bbd2 100644
--- a/indra/llmessage/tests/commtest.h
+++ b/indra/llmessage/tests/commtest.h
@@ -33,15 +33,15 @@
#include "llevents.h"
#include "llsd.h"
#include "llhost.h"
+#include "llexception.h"
#include "stringize.h"
#include <map>
#include <string>
-#include <stdexcept>
#include <boost/lexical_cast.hpp>
-struct CommtestError: public std::runtime_error
+struct CommtestError: public LLException
{
- CommtestError(const std::string& what): std::runtime_error(what) {}
+ CommtestError(const std::string& what): LLException(what) {}
};
static bool query_verbose()
@@ -68,7 +68,7 @@ static int query_port(const std::string& var)
const char* cport = getenv(var.c_str());
if (! cport)
{
- throw CommtestError(STRINGIZE("missing environment variable" << var));
+ LLTHROW(CommtestError(STRINGIZE("missing environment variable" << var)));
}
// This will throw, too, if the value of PORT isn't numeric.
int port(boost::lexical_cast<int>(cport));
diff --git a/indra/llmessage/tests/networkio.h b/indra/llmessage/tests/networkio.h
index 2aff90ca1e..5eb739393f 100644
--- a/indra/llmessage/tests/networkio.h
+++ b/indra/llmessage/tests/networkio.h
@@ -34,6 +34,7 @@
#include "llares.h"
#include "llpumpio.h"
#include "llhttpclient.h"
+#include "llexception.h"
/*****************************************************************************
* NetworkIO
@@ -51,7 +52,7 @@ public:
ll_init_apr();
if (! gAPRPoolp)
{
- throw std::runtime_error("Can't initialize APR");
+ LLTHROW(LLException("Can't initialize APR"));
}
// Create IO Pump to use for HTTP Requests.
@@ -59,7 +60,7 @@ public:
LLHTTPClient::setPump(*mServicePump);
if (ll_init_ares() == NULL || !gAres->isInitialized())
{
- throw std::runtime_error("Can't start DNS resolver");
+ LLTHROW(LLException("Can't start DNS resolver"));
}
// You can interrupt pump() without waiting the full timeout duration
diff --git a/indra/llrender/llimagegl.cpp b/indra/llrender/llimagegl.cpp
index ebed454271..01c1d81823 100644
--- a/indra/llrender/llimagegl.cpp
+++ b/indra/llrender/llimagegl.cpp
@@ -240,6 +240,15 @@ S32 LLImageGL::dataFormatComponents(S32 dataformat)
//----------------------------------------------------------------------------
+#if LL_LINUX
+// gcc 4.7.2 produces this error for the following function, which nat has
+// been unable to diagnose as an actual problem:
+// llimagegl.cpp:247:2: error: '<anonymous>.LLTrace::BlockTimer::mStartTime'
+// may be used uninitialized in this function [-Werror=uninitialized]
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wuninitialized"
+#endif
+
static LLTrace::BlockTimerStatHandle FTM_IMAGE_UPDATE_STATS("Image Stats");
// static
void LLImageGL::updateStats(F32 current_time)
@@ -250,6 +259,11 @@ void LLImageGL::updateStats(F32 current_time)
sCurBoundTextureMemory = S32Bytes(0);
}
+#if LL_LINUX
+// In general we do want to know about uninitialized variables!
+#pragma GCC diagnostic pop
+#endif
+
//static
S32 LLImageGL::updateBoundTexMem(const S32Bytes mem, const S32 ncomponents, S32 category)
{
diff --git a/indra/llwindow/llappdelegate-objc.h b/indra/llwindow/llappdelegate-objc.h
index faa5d3abb7..6daf1ac55b 100644
--- a/indra/llwindow/llappdelegate-objc.h
+++ b/indra/llwindow/llappdelegate-objc.h
@@ -41,7 +41,7 @@
@property (retain) NSString *currentInputLanguage;
-- (void) mainLoop;
+- (void) oneFrame;
- (void) showInputWindow:(bool)show withEvent:(NSEvent*)textEvent;
- (void) languageUpdated;
- (bool) romanScript;
diff --git a/indra/llwindow/llwindowmacosx-objc.h b/indra/llwindow/llwindowmacosx-objc.h
index c22f3382fb..b06cd2c184 100644
--- a/indra/llwindow/llwindowmacosx-objc.h
+++ b/indra/llwindow/llwindowmacosx-objc.h
@@ -69,7 +69,7 @@ typedef const NativeKeyEventData * NSKeyEventRef;
// These are defined in llappviewermacosx.cpp.
bool initViewer();
void handleQuit();
-bool runMainLoop();
+bool pumpMainLoop();
void initMainLoop();
void cleanupViewer();
void handleUrl(const char* url);
diff --git a/indra/mac_crash_logger/llcrashloggermac.cpp b/indra/mac_crash_logger/llcrashloggermac.cpp
index 3149fad6e8..ec3616e26a 100644
--- a/indra/mac_crash_logger/llcrashloggermac.cpp
+++ b/indra/mac_crash_logger/llcrashloggermac.cpp
@@ -64,7 +64,7 @@ void LLCrashLoggerMac::gatherPlatformSpecificFiles()
{
}
-bool LLCrashLoggerMac::mainLoop()
+bool LLCrashLoggerMac::frame()
{
if (mCrashBehavior == CRASH_BEHAVIOR_ALWAYS_SEND)
diff --git a/indra/mac_crash_logger/llcrashloggermac.h b/indra/mac_crash_logger/llcrashloggermac.h
index 6d8f63ecac..05ef8c9f53 100644
--- a/indra/mac_crash_logger/llcrashloggermac.h
+++ b/indra/mac_crash_logger/llcrashloggermac.h
@@ -37,7 +37,7 @@ public:
LLCrashLoggerMac(void);
~LLCrashLoggerMac(void);
virtual bool init();
- virtual bool mainLoop();
+ virtual bool frame();
virtual bool cleanup();
virtual void gatherPlatformSpecificFiles();
};
diff --git a/indra/mac_crash_logger/mac_crash_logger.cpp b/indra/mac_crash_logger/mac_crash_logger.cpp
index 95d4e65207..54e41a1954 100644
--- a/indra/mac_crash_logger/mac_crash_logger.cpp
+++ b/indra/mac_crash_logger/mac_crash_logger.cpp
@@ -49,7 +49,7 @@ int main(int argc, char **argv)
{
// return NSApplicationMain(argc, (const char **)argv);
}
- app.mainLoop();
+ app.frame();
app.cleanup();
LL_INFOS() << "Crash reporter finished normally." << LL_ENDL;
diff --git a/indra/newview/llaccountingcostmanager.cpp b/indra/newview/llaccountingcostmanager.cpp
index 92a5413adb..1dddf52961 100644
--- a/indra/newview/llaccountingcostmanager.cpp
+++ b/indra/newview/llaccountingcostmanager.cpp
@@ -31,6 +31,8 @@
#include "llcoros.h"
#include "lleventcoro.h"
#include "llcorehttputil.h"
+#include "llexception.h"
+#include "stringize.h"
#include <algorithm>
#include <iterator>
@@ -154,13 +156,11 @@ void LLAccountingCostManager::accountingCostCoro(std::string url,
} while (false);
}
- catch (std::exception e)
- {
- LL_WARNS() << "Caught exception '" << e.what() << "'" << LL_ENDL;
- }
catch (...)
{
- LL_WARNS() << "Caught unknown exception." << LL_ENDL;
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName()
+ << "('" << url << "')"));
+ throw;
}
mPendingObjectQuota.clear();
diff --git a/indra/newview/llappcorehttp.cpp b/indra/newview/llappcorehttp.cpp
index 49291ea564..c1f898284a 100644
--- a/indra/newview/llappcorehttp.cpp
+++ b/indra/newview/llappcorehttp.cpp
@@ -30,6 +30,8 @@
#include "llappviewer.h"
#include "llviewercontrol.h"
+#include "llexception.h"
+#include "stringize.h"
#include <openssl/x509_vfy.h>
#include <openssl/ssl.h>
@@ -534,7 +536,7 @@ LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url,
// somewhat clumsy, as we may run into errors that do not map directly to curl
// error codes. Should be refactored with login refactoring, perhaps.
result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CACERT);
- result.setMessage(cert_exception.getMessage());
+ result.setMessage(cert_exception.what());
LLPointer<LLCertificate> cert = cert_exception.getCert();
cert->ref(); // adding an extra ref here
result.setErrorData(cert.get());
@@ -544,13 +546,14 @@ LLCore::HttpStatus LLAppCoreHttp::sslVerify(const std::string &url,
catch (LLCertException &cert_exception)
{
result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_PEER_CERTIFICATE);
- result.setMessage(cert_exception.getMessage());
+ result.setMessage(cert_exception.what());
LLPointer<LLCertificate> cert = cert_exception.getCert();
cert->ref(); // adding an extra ref here
result.setErrorData(cert.get());
}
catch (...)
{
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("('" << url << "')"));
// any other odd error, we just handle as a connect error.
result = LLCore::HttpStatus(LLCore::HttpStatus::EXT_CURL_EASY, CURLE_SSL_CONNECT_ERROR);
}
diff --git a/indra/newview/llappdelegate-objc.mm b/indra/newview/llappdelegate-objc.mm
index 549df80fa1..be8877328d 100644
--- a/indra/newview/llappdelegate-objc.mm
+++ b/indra/newview/llappdelegate-objc.mm
@@ -48,16 +48,19 @@
- (void) applicationDidFinishLaunching:(NSNotification *)notification
{
frameTimer = nil;
-
+
[self languageUpdated];
-
+
if (initViewer())
{
- frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self selector:@selector(mainLoop) userInfo:nil repeats:YES];
+ // Set up recurring calls to oneFrame (repeating timer with timeout 0)
+ // until applicationShouldTerminate.
+ frameTimer = [NSTimer scheduledTimerWithTimeInterval:0.0 target:self
+ selector:@selector(oneFrame) userInfo:nil repeats:YES];
} else {
handleQuit();
}
-
+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(languageUpdated) name:@"NSTextInputContextKeyboardSelectionDidChangeNotification" object:nil];
// [[NSAppleEventManager sharedAppleEventManager] setEventHandler:self andSelector:@selector(handleGetURLEvent:withReplyEvent:) forEventClass:kInternetEventClass andEventID:kAEGetURL];
@@ -96,22 +99,29 @@
- (NSApplicationDelegateReply) applicationShouldTerminate:(NSApplication *)sender
{
- if (!runMainLoop())
+ // run one frame to assess state
+ if (!pumpMainLoop())
{
+ // pumpMainLoop() returns true when done, false if it wants to be
+ // called again. Since it returned false, do not yet cancel
+ // frameTimer.
handleQuit();
return NSTerminateCancel;
} else {
+ // pumpMainLoop() returned true: it's done. Okay, done with frameTimer.
[frameTimer release];
cleanupViewer();
return NSTerminateNow;
}
}
-- (void) mainLoop
+- (void) oneFrame
{
- bool appExiting = runMainLoop();
+ bool appExiting = pumpMainLoop();
if (appExiting)
{
+ // Once pumpMainLoop() reports that we're done, cancel frameTimer:
+ // stop the repetitive calls.
[frameTimer release];
[[NSApplication sharedApplication] terminate:self];
}
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 05f2f75a67..3023944f6e 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -122,6 +122,7 @@
#include "llleap.h"
#include "stringize.h"
#include "llcoros.h"
+#include "llexception.h"
#if !LL_LINUX
#include "cef/llceflib.h"
#if LL_WINDOWS
@@ -234,7 +235,6 @@
#include "llcoproceduremanager.h"
#include "llviewereventrecorder.h"
-
// *FIX: These extern globals should be cleaned up.
// The globals either represent state/config/resource-storage of either
// this app, or another 'component' of the viewer. App globals should be
@@ -772,9 +772,6 @@ bool LLAppViewer::init()
//
// Start of the application
//
-#ifdef LL_DARWIN
- mMainLoopInitialized = false;
-#endif
// initialize LLWearableType translation bridge.
// Memory will be cleaned up in ::cleanupClass()
@@ -1223,6 +1220,23 @@ bool LLAppViewer::init()
boost::bind(&LLControlGroup::getU32, boost::ref(gSavedSettings), _1),
boost::bind(&LLControlGroup::declareU32, boost::ref(gSavedSettings), _1, _2, _3, LLControlVariable::PERSIST_ALWAYS));
+ /*----------------------------------------------------------------------*/
+ // nat 2016-06-29 moved the following here from the former mainLoop().
+ mMainloopTimeout = new LLWatchdogTimeout();
+
+ // Create IO Pump to use for HTTP Requests.
+ gServicePump = new LLPumpIO(gAPRPoolp);
+
+ // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.
+
+ LLVoiceChannel::initClass();
+ LLVoiceClient::getInstance()->init(gServicePump);
+ LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true);
+
+ joystick = LLViewerJoystick::getInstance();
+ joystick->setNeedsReset(true);
+ /*----------------------------------------------------------------------*/
+
return true;
}
@@ -1297,299 +1311,265 @@ static LLTrace::BlockTimerStatHandle FTM_AGENT_UPDATE("Update");
// externally visible timers
LLTrace::BlockTimerStatHandle FTM_FRAME("Frame");
-bool LLAppViewer::mainLoop()
+bool LLAppViewer::frame()
{
-#ifdef LL_DARWIN
- if (!mMainLoopInitialized)
-#endif
- {
- LL_INFOS() << "Entering main_loop" << LL_ENDL;
- mMainloopTimeout = new LLWatchdogTimeout();
-
- //-------------------------------------------
- // Run main loop until time to quit
- //-------------------------------------------
-
- // Create IO Pump to use for HTTP Requests.
- gServicePump = new LLPumpIO(gAPRPoolp);
-
- // Note: this is where gLocalSpeakerMgr and gActiveSpeakerMgr used to be instantiated.
-
- LLVoiceChannel::initClass();
- LLVoiceClient::getInstance()->init(gServicePump);
- LLVoiceChannel::setCurrentVoiceChannelChangedCallback(boost::bind(&LLFloaterIMContainer::onCurrentChannelChanged, _1), true);
-
- joystick = LLViewerJoystick::getInstance();
- joystick->setNeedsReset(true);
-
-#ifdef LL_DARWIN
- // Ensure that this section of code never gets called again on OS X.
- mMainLoopInitialized = true;
-#endif
- }
- // As we do not (yet) send data on the mainloop LLEventPump that varies
- // with each frame, no need to instantiate a new LLSD event object each
- // time. Obviously, if that changes, just instantiate the LLSD at the
- // point of posting.
-
LLEventPump& mainloop(LLEventPumps::instance().obtain("mainloop"));
-
- LLSD newFrame;
-
+ LLSD newFrame;
+
LLTimer frameTimer,idleTimer;
LLTimer debugTime;
-
+
//LLPrivateMemoryPoolTester::getInstance()->run(false) ;
//LLPrivateMemoryPoolTester::getInstance()->run(true) ;
//LLPrivateMemoryPoolTester::destroy() ;
- // Handle messages
-#ifdef LL_DARWIN
- if (!LLApp::isExiting())
-#else
- while (!LLApp::isExiting())
-#endif
+ LL_RECORD_BLOCK_TIME(FTM_FRAME);
+ LLTrace::BlockTimer::processTimes();
+ LLTrace::get_frame_recording().nextPeriod();
+ LLTrace::BlockTimer::logStats();
+
+ LLTrace::get_thread_recorder()->pullFromChildren();
+
+ //clear call stack records
+ LL_CLEAR_CALLSTACKS();
+
+ //check memory availability information
+ checkMemory() ;
+
+ try
{
- LL_RECORD_BLOCK_TIME(FTM_FRAME);
- LLTrace::BlockTimer::processTimes();
- LLTrace::get_frame_recording().nextPeriod();
- LLTrace::BlockTimer::logStats();
+ pingMainloopTimeout("Main:MiscNativeWindowEvents");
- LLTrace::get_thread_recorder()->pullFromChildren();
+ if (gViewerWindow)
+ {
+ LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
+ gViewerWindow->getWindow()->processMiscNativeEvents();
+ }
- //clear call stack records
- LL_CLEAR_CALLSTACKS();
+ pingMainloopTimeout("Main:GatherInput");
- //check memory availability information
- checkMemory() ;
+ if (gViewerWindow)
+ {
+ LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
+ if (!restoreErrorTrap())
+ {
+ LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL;
+ }
+
+ gViewerWindow->getWindow()->gatherInput();
+ }
+
+#if 1 && !LL_RELEASE_FOR_DOWNLOAD
+ // once per second debug info
+ if (debugTime.getElapsedTimeF32() > 1.f)
+ {
+ debugTime.reset();
+ }
- try
+#endif
+ //memory leaking simulation
+ LLFloaterMemLeak* mem_leak_instance =
+ LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
+ if(mem_leak_instance)
{
- pingMainloopTimeout("Main:MiscNativeWindowEvents");
+ mem_leak_instance->idle() ;
+ }
+
+ // canonical per-frame event
+ mainloop.post(newFrame);
- if (gViewerWindow)
+ if (!LLApp::isExiting())
+ {
+ pingMainloopTimeout("Main:JoystickKeyboard");
+
+ // Scan keyboard for movement keys. Command keys and typing
+ // are handled by windows callbacks. Don't do this until we're
+ // done initializing. JC
+ if ((gHeadlessClient || gViewerWindow->getWindow()->getVisible())
+ && gViewerWindow->getActive()
+ && !gViewerWindow->getWindow()->getMinimized()
+ && LLStartUp::getStartupState() == STATE_STARTED
+ && (gHeadlessClient || !gViewerWindow->getShowProgress())
+ && !gFocusMgr.focusLocked())
{
- LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
- gViewerWindow->getWindow()->processMiscNativeEvents();
+ joystick->scanJoystick();
+ gKeyboard->scanKeyboard();
}
-
- pingMainloopTimeout("Main:GatherInput");
-
- if (gViewerWindow)
+
+ // Update state based on messages, user input, object idle.
{
- LL_RECORD_BLOCK_TIME(FTM_MESSAGES);
- if (!restoreErrorTrap())
- {
- LL_WARNS() << " Someone took over my signal/exception handler (post messagehandling)!" << LL_ENDL;
- }
+ pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds!
+
+ LL_RECORD_BLOCK_TIME(FTM_IDLE);
+ idle();
- gViewerWindow->getWindow()->gatherInput();
+ resumeMainloopTimeout();
}
-#if 1 && !LL_RELEASE_FOR_DOWNLOAD
- // once per second debug info
- if (debugTime.getElapsedTimeF32() > 1.f)
+ if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED))
{
- debugTime.reset();
+ pauseMainloopTimeout();
+ saveFinalSnapshot();
+ disconnectViewer();
+ resumeMainloopTimeout();
}
-
-#endif
- //memory leaking simulation
- LLFloaterMemLeak* mem_leak_instance =
- LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
- if(mem_leak_instance)
- {
- mem_leak_instance->idle() ;
- }
-
- // canonical per-frame event
- mainloop.post(newFrame);
- if (!LLApp::isExiting())
+ // Render scene.
+ // *TODO: Should we run display() even during gHeadlessClient? DK 2011-02-18
+ if (!LLApp::isExiting() && !gHeadlessClient)
{
- pingMainloopTimeout("Main:JoystickKeyboard");
-
- // Scan keyboard for movement keys. Command keys and typing
- // are handled by windows callbacks. Don't do this until we're
- // done initializing. JC
- if ((gHeadlessClient || gViewerWindow->getWindow()->getVisible())
- && gViewerWindow->getActive()
- && !gViewerWindow->getWindow()->getMinimized()
- && LLStartUp::getStartupState() == STATE_STARTED
- && (gHeadlessClient || !gViewerWindow->getShowProgress())
- && !gFocusMgr.focusLocked())
- {
- joystick->scanJoystick();
- gKeyboard->scanKeyboard();
- }
+ pingMainloopTimeout("Main:Display");
+ gGLActive = TRUE;
+ display();
+ pingMainloopTimeout("Main:Snapshot");
+ LLFloaterSnapshot::update(); // take snapshots
+ gGLActive = FALSE;
+ }
+ }
- // Update state based on messages, user input, object idle.
- {
- pauseMainloopTimeout(); // *TODO: Remove. Messages shouldn't be stalling for 20+ seconds!
-
- LL_RECORD_BLOCK_TIME(FTM_IDLE);
- idle();
+ pingMainloopTimeout("Main:Sleep");
- resumeMainloopTimeout();
- }
-
- if (gDoDisconnect && (LLStartUp::getStartupState() == STATE_STARTED))
- {
- pauseMainloopTimeout();
- saveFinalSnapshot();
- disconnectViewer();
- resumeMainloopTimeout();
- }
+ pauseMainloopTimeout();
+
+ // Sleep and run background threads
+ {
+ LL_RECORD_BLOCK_TIME(FTM_SLEEP);
+
+ // yield some time to the os based on command line option
+ if(mYieldTime >= 0)
+ {
+ LL_RECORD_BLOCK_TIME(FTM_YIELD);
+ ms_sleep(mYieldTime);
+ }
- // Render scene.
- // *TODO: Should we run display() even during gHeadlessClient? DK 2011-02-18
- if (!LLApp::isExiting() && !gHeadlessClient)
+ // yield cooperatively when not running as foreground window
+ if ( (gViewerWindow && !gViewerWindow->getWindow()->getVisible())
+ || !gFocusMgr.getAppHasFocus())
+ {
+ // Sleep if we're not rendering, or the window is minimized.
+ S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000);
+ // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads
+ // of equal priority on Windows
+ if (milliseconds_to_sleep > 0)
{
- pingMainloopTimeout("Main:Display");
- gGLActive = TRUE;
- display();
- pingMainloopTimeout("Main:Snapshot");
- LLFloaterSnapshot::update(); // take snapshots
- gGLActive = FALSE;
+ ms_sleep(milliseconds_to_sleep);
+ // also pause worker threads during this wait period
+ LLAppViewer::getTextureCache()->pause();
+ LLAppViewer::getImageDecodeThread()->pause();
}
}
-
- pingMainloopTimeout("Main:Sleep");
- pauseMainloopTimeout();
+ if (mRandomizeFramerate)
+ {
+ ms_sleep(rand() % 200);
+ }
- // Sleep and run background threads
+ if (mPeriodicSlowFrame
+ && (gFrameCount % 10 == 0))
{
- LL_RECORD_BLOCK_TIME(FTM_SLEEP);
-
- // yield some time to the os based on command line option
- if(mYieldTime >= 0)
- {
- LL_RECORD_BLOCK_TIME(FTM_YIELD);
- ms_sleep(mYieldTime);
- }
+ LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL;
+ ms_sleep(500);
+ }
+
+ const F64Milliseconds max_idle_time = llmin(.005f*10.f*(F32Milliseconds)gFrameTimeSeconds, F32Milliseconds(5)); // 5 ms a second
+ idleTimer.reset();
+ S32 total_work_pending = 0;
+ S32 total_io_pending = 0;
+ while(1)
+ {
+ S32 work_pending = 0;
+ S32 io_pending = 0;
+ F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f);
+
+ work_pending += updateTextureThreads(max_time);
- // yield cooperatively when not running as foreground window
- if ( (gViewerWindow && !gViewerWindow->getWindow()->getVisible())
- || !gFocusMgr.getAppHasFocus())
{
- // Sleep if we're not rendering, or the window is minimized.
- S32 milliseconds_to_sleep = llclamp(gSavedSettings.getS32("BackgroundYieldTime"), 0, 1000);
- // don't sleep when BackgroundYieldTime set to 0, since this will still yield to other threads
- // of equal priority on Windows
- if (milliseconds_to_sleep > 0)
- {
- ms_sleep(milliseconds_to_sleep);
- // also pause worker threads during this wait period
- LLAppViewer::getTextureCache()->pause();
- LLAppViewer::getImageDecodeThread()->pause();
- }
+ LL_RECORD_BLOCK_TIME(FTM_VFS);
+ io_pending += LLVFSThread::updateClass(1);
}
-
- if (mRandomizeFramerate)
{
- ms_sleep(rand() % 200);
+ LL_RECORD_BLOCK_TIME(FTM_LFS);
+ io_pending += LLLFSThread::updateClass(1);
}
- if (mPeriodicSlowFrame
- && (gFrameCount % 10 == 0))
+ if (io_pending > 1000)
{
- LL_INFOS() << "Periodic slow frame - sleeping 500 ms" << LL_ENDL;
- ms_sleep(500);
+ ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up
}
- const F64Milliseconds max_idle_time = llmin(.005f*10.f*(F32Milliseconds)gFrameTimeSeconds, F32Milliseconds(5)); // 5 ms a second
- idleTimer.reset();
- S32 total_work_pending = 0;
- S32 total_io_pending = 0;
- while(1)
- {
- S32 work_pending = 0;
- S32 io_pending = 0;
- F32 max_time = llmin(gFrameIntervalSeconds.value() *10.f, 1.f);
-
- work_pending += updateTextureThreads(max_time);
-
- {
- LL_RECORD_BLOCK_TIME(FTM_VFS);
- io_pending += LLVFSThread::updateClass(1);
- }
- {
- LL_RECORD_BLOCK_TIME(FTM_LFS);
- io_pending += LLLFSThread::updateClass(1);
- }
-
- if (io_pending > 1000)
- {
- ms_sleep(llmin(io_pending/100,100)); // give the vfs some time to catch up
- }
-
- total_work_pending += work_pending ;
- total_io_pending += io_pending ;
-
- if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time)
- {
- break;
- }
- }
- gMeshRepo.update() ;
+ total_work_pending += work_pending ;
+ total_io_pending += io_pending ;
- if(!total_work_pending) //pause texture fetching threads if nothing to process.
+ if (!work_pending || idleTimer.getElapsedTimeF64() >= max_idle_time)
{
- LLAppViewer::getTextureCache()->pause();
- LLAppViewer::getImageDecodeThread()->pause();
- LLAppViewer::getTextureFetch()->pause();
+ break;
}
- if(!total_io_pending) //pause file threads if nothing to process.
- {
- LLVFSThread::sLocal->pause();
- LLLFSThread::sLocal->pause();
- }
+ }
+ gMeshRepo.update() ;
+
+ if(!total_work_pending) //pause texture fetching threads if nothing to process.
+ {
+ LLAppViewer::getTextureCache()->pause();
+ LLAppViewer::getImageDecodeThread()->pause();
+ LLAppViewer::getTextureFetch()->pause();
+ }
+ if(!total_io_pending) //pause file threads if nothing to process.
+ {
+ LLVFSThread::sLocal->pause();
+ LLLFSThread::sLocal->pause();
+ }
- //texture fetching debugger
- if(LLTextureFetchDebugger::isEnabled())
+ //texture fetching debugger
+ if(LLTextureFetchDebugger::isEnabled())
+ {
+ LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance =
+ LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger");
+ if(tex_fetch_debugger_instance)
{
- LLFloaterTextureFetchDebugger* tex_fetch_debugger_instance =
- LLFloaterReg::findTypedInstance<LLFloaterTextureFetchDebugger>("tex_fetch_debugger");
- if(tex_fetch_debugger_instance)
- {
- tex_fetch_debugger_instance->idle() ;
- }
+ tex_fetch_debugger_instance->idle() ;
}
+ }
- if ((LLStartUp::getStartupState() >= STATE_CLEANUP) &&
- (frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD))
- {
- gFrameStalls++;
- }
- frameTimer.reset();
+ if ((LLStartUp::getStartupState() >= STATE_CLEANUP) &&
+ (frameTimer.getElapsedTimeF64() > FRAME_STALL_THRESHOLD))
+ {
+ gFrameStalls++;
+ }
+ frameTimer.reset();
- resumeMainloopTimeout();
-
- pingMainloopTimeout("Main:End");
- }
+ resumeMainloopTimeout();
+
+ pingMainloopTimeout("Main:End");
}
- catch(std::bad_alloc)
- {
- LLMemory::logMemoryInfo(TRUE) ;
+ }
+ catch (const LLContinueError&)
+ {
+ LOG_UNHANDLED_EXCEPTION("");
+ }
+ catch(std::bad_alloc)
+ {
+ LLMemory::logMemoryInfo(TRUE) ;
- //stop memory leaking simulation
- LLFloaterMemLeak* mem_leak_instance =
- LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
- if(mem_leak_instance)
- {
- mem_leak_instance->stop() ;
- LL_WARNS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ;
- }
- else
- {
- //output possible call stacks to log file.
- LLError::LLCallStacks::print() ;
+ //stop memory leaking simulation
+ LLFloaterMemLeak* mem_leak_instance =
+ LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
+ if(mem_leak_instance)
+ {
+ mem_leak_instance->stop() ;
+ LL_WARNS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL ;
+ }
+ else
+ {
+ //output possible call stacks to log file.
+ LLError::LLCallStacks::print() ;
- LL_ERRS() << "Bad memory allocation in LLAppViewer::mainLoop()!" << LL_ENDL ;
- }
+ LL_ERRS() << "Bad memory allocation in LLAppViewer::frame()!" << LL_ENDL ;
}
}
+ catch (...)
+ {
+ CRASH_ON_UNHANDLED_EXCEPTION("");
+ }
if (LLApp::isExiting())
{
@@ -1603,7 +1583,7 @@ bool LLAppViewer::mainLoop()
catch(std::bad_alloc)
{
LL_WARNS() << "Bad memory allocation when saveFinalSnapshot() is called!" << LL_ENDL ;
-
+
//stop memory leaking simulation
LLFloaterMemLeak* mem_leak_instance =
LLFloaterReg::findTypedInstance<LLFloaterMemLeak>("mem_leaking");
@@ -1612,16 +1592,20 @@ bool LLAppViewer::mainLoop()
mem_leak_instance->stop() ;
}
}
+ catch (...)
+ {
+ CRASH_ON_UNHANDLED_EXCEPTION("saveFinalSnapshot()");
+ }
}
-
+
delete gServicePump;
-
+
destroyMainloopTimeout();
-
+
LL_INFOS() << "Exiting main_loop" << LL_ENDL;
}
- return LLApp::isExiting();
+ return ! LLApp::isRunning();
}
S32 LLAppViewer::updateTextureThreads(F32 max_time)
@@ -5562,8 +5546,7 @@ void LLAppViewer::forceErrorInfiniteLoop()
void LLAppViewer::forceErrorSoftwareException()
{
LL_WARNS() << "Forcing a deliberate exception" << LL_ENDL;
- // *FIX: Any way to insure it won't be handled?
- throw;
+ LLTHROW(LLException("User selected Force Software Exception"));
}
void LLAppViewer::forceErrorDriverCrash()
diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h
index b5e674bd7b..f7c1bb58b4 100644
--- a/indra/newview/llappviewer.h
+++ b/indra/newview/llappviewer.h
@@ -79,7 +79,7 @@ public:
//
virtual bool init(); // Override to do application initialization
virtual bool cleanup(); // Override to do application cleanup
- virtual bool mainLoop(); // Override for the application main loop. Needs to at least gracefully notice the QUITTING state and exit.
+ virtual bool frame(); // Override for application body logic
// Application control
void flushVFSIO(); // waits for vfs transfers to complete
@@ -283,7 +283,6 @@ private:
std::string mSerialNumber;
bool mPurgeCache;
bool mPurgeOnExit;
- bool mMainLoopInitialized;
LLViewerJoystick* joystick;
bool mSavedFinalSnapshot;
diff --git a/indra/newview/llappviewerlinux.cpp b/indra/newview/llappviewerlinux.cpp
index f5742b29cf..6f32aab851 100644
--- a/indra/newview/llappviewerlinux.cpp
+++ b/indra/newview/llappviewerlinux.cpp
@@ -95,10 +95,8 @@ int main( int argc, char **argv )
}
// Run the application main loop
- if(!LLApp::isQuitting())
- {
- viewer_app_ptr->mainLoop();
- }
+ while (! viewer_app_ptr->frame())
+ {}
if (!LLApp::isError())
{
diff --git a/indra/newview/llappviewermacosx.cpp b/indra/newview/llappviewermacosx.cpp
index ca219fda59..4fe1e31668 100644
--- a/indra/newview/llappviewermacosx.cpp
+++ b/indra/newview/llappviewermacosx.cpp
@@ -117,12 +117,17 @@ void handleQuit()
LLAppViewer::instance()->userQuit();
}
-bool runMainLoop()
+// This function is called pumpMainLoop() rather than runMainLoop() because
+// it passes control to the viewer's main-loop logic for a single frame. Like
+// LLAppViewer::frame(), it returns 'true' when it's done. Until then, it
+// expects to be called again by the timer in LLAppDelegate
+// (llappdelegate-objc.mm).
+bool pumpMainLoop()
{
bool ret = LLApp::isQuitting();
if (!ret && gViewerAppPtr != NULL)
{
- ret = gViewerAppPtr->mainLoop();
+ ret = gViewerAppPtr->frame();
} else {
ret = true;
}
diff --git a/indra/newview/llappviewerwin32.cpp b/indra/newview/llappviewerwin32.cpp
index 4786f83bfd..af0cd27fd5 100644
--- a/indra/newview/llappviewerwin32.cpp
+++ b/indra/newview/llappviewerwin32.cpp
@@ -317,10 +317,8 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
}
// Run the application main loop
- if(!LLApp::isQuitting())
- {
- viewer_app_ptr->mainLoop();
- }
+ while (! viewer_app_ptr->frame())
+ {}
if (!LLApp::isError())
{
@@ -330,33 +328,33 @@ int APIENTRY WINMAIN(HINSTANCE hInstance,
// app cleanup if there was a problem.
//
#if WINDOWS_CRT_MEM_CHECKS
- LL_INFOS() << "CRT Checking memory:" << LL_ENDL;
- if (!_CrtCheckMemory())
- {
- LL_WARNS() << "_CrtCheckMemory() failed at prior to cleanup!" << LL_ENDL;
- }
- else
- {
- LL_INFOS() << " No corruption detected." << LL_ENDL;
- }
+ LL_INFOS() << "CRT Checking memory:" << LL_ENDL;
+ if (!_CrtCheckMemory())
+ {
+ LL_WARNS() << "_CrtCheckMemory() failed at prior to cleanup!" << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << " No corruption detected." << LL_ENDL;
+ }
#endif
-
- gGLActive = TRUE;
- viewer_app_ptr->cleanup();
-
+ gGLActive = TRUE;
+
+ viewer_app_ptr->cleanup();
+
#if WINDOWS_CRT_MEM_CHECKS
- LL_INFOS() << "CRT Checking memory:" << LL_ENDL;
- if (!_CrtCheckMemory())
- {
- LL_WARNS() << "_CrtCheckMemory() failed after cleanup!" << LL_ENDL;
- }
- else
- {
- LL_INFOS() << " No corruption detected." << LL_ENDL;
- }
+ LL_INFOS() << "CRT Checking memory:" << LL_ENDL;
+ if (!_CrtCheckMemory())
+ {
+ LL_WARNS() << "_CrtCheckMemory() failed after cleanup!" << LL_ENDL;
+ }
+ else
+ {
+ LL_INFOS() << " No corruption detected." << LL_ENDL;
+ }
#endif
-
+
}
delete viewer_app_ptr;
viewer_app_ptr = NULL;
diff --git a/indra/newview/llcommandlineparser.cpp b/indra/newview/llcommandlineparser.cpp
index 1819fc74ee..90a5483dc9 100644
--- a/indra/newview/llcommandlineparser.cpp
+++ b/indra/newview/llcommandlineparser.cpp
@@ -26,6 +26,7 @@
#include "llviewerprecompiledheaders.h"
#include "llcommandlineparser.h"
+#include "llexception.h"
// *NOTE: The boost::lexical_cast generates
// the warning C4701(local used with out assignment) in VC7.1.
@@ -50,6 +51,7 @@
#include "llsdserialize.h"
#include "llerror.h"
#include "stringize.h"
+#include "llexception.h"
#include <string>
#include <set>
#include <iostream>
@@ -98,14 +100,14 @@ namespace
bool gPastLastOption = false;
}
-class LLCLPError : public std::logic_error {
+class LLCLPError : public LLException {
public:
- LLCLPError(const std::string& what) : std::logic_error(what) {}
+ LLCLPError(const std::string& what) : LLException(what) {}
};
-class LLCLPLastOption : public std::logic_error {
+class LLCLPLastOption : public LLException {
public:
- LLCLPLastOption(const std::string& what) : std::logic_error(what) {}
+ LLCLPLastOption(const std::string& what) : LLException(what) {}
};
class LLCLPValue : public po::value_semantic_codecvt_helper<char>
@@ -202,17 +204,17 @@ protected:
{
if(gPastLastOption)
{
- throw(LLCLPLastOption("Don't parse no more!"));
+ LLTHROW(LLCLPLastOption("Don't parse no more!"));
}
// Error checks. Needed?
if (!value_store.empty() && !is_composing())
{
- throw(LLCLPError("Non composing value with multiple occurences."));
+ LLTHROW(LLCLPError("Non composing value with multiple occurences."));
}
if (new_tokens.size() < min_tokens() || new_tokens.size() > max_tokens())
{
- throw(LLCLPError("Illegal number of tokens specified."));
+ LLTHROW(LLCLPError("Illegal number of tokens specified."));
}
if(value_store.empty())
@@ -466,7 +468,7 @@ onevalue(const std::string& option,
{
// What does it mean when the user specifies a command-line switch
// that requires a value, but omits the value? Complain.
- throw LLCLPError(STRINGIZE("No value specified for --" << option << "!"));
+ LLTHROW(LLCLPError(STRINGIZE("No value specified for --" << option << "!")));
}
else if (value.size() > 1)
{
@@ -484,9 +486,9 @@ void badvalue(const std::string& option,
// If the user passes an unusable value for a command-line switch, it
// seems like a really bad idea to just ignore it, even with a log
// warning.
- throw LLCLPError(STRINGIZE("Invalid value specified by command-line switch '" << option
- << "' for variable '" << varname << "' of type " << type
- << ": '" << value << "'"));
+ LLTHROW(LLCLPError(STRINGIZE("Invalid value specified by command-line switch '" << option
+ << "' for variable '" << varname << "' of type " << type
+ << ": '" << value << "'")));
}
template <typename T>
diff --git a/indra/newview/llsecapi.cpp b/indra/newview/llsecapi.cpp
index 4f9f83b6f2..72d7cf1e45 100644
--- a/indra/newview/llsecapi.cpp
+++ b/indra/newview/llsecapi.cpp
@@ -29,6 +29,8 @@
#include "llviewerprecompiledheaders.h"
#include "llsecapi.h"
#include "llsechandler_basic.h"
+#include "llexception.h"
+#include "stringize.h"
#include <openssl/evp.h>
#include <openssl/err.h>
#include <map>
@@ -64,12 +66,12 @@ void initializeSecHandler()
}
catch (LLProtectedDataException e)
{
- exception_msg = e.getMessage();
+ exception_msg = e.what();
}
}
if (!exception_msg.empty()) // an exception was thrown.
{
- throw LLProtectedDataException(exception_msg.c_str());
+ LLTHROW(LLProtectedDataException(exception_msg));
}
}
@@ -101,6 +103,7 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred)
LLSD LLCredential::getLoginParams()
{
LLSD result = LLSD::emptyMap();
+ std::string username;
try
{
if (mIdentifier["type"].asString() == "agent")
@@ -109,17 +112,19 @@ LLSD LLCredential::getLoginParams()
result["passwd"] = "$1$" + mAuthenticator["secret"].asString();
result["first"] = mIdentifier["first_name"];
result["last"] = mIdentifier["last_name"];
-
+ username = result["first"].asString() + " " + result["last"].asString();
}
else if (mIdentifier["type"].asString() == "account")
{
result["username"] = mIdentifier["account_name"];
result["passwd"] = mAuthenticator["secret"];
-
+ username = result["username"].asString();
}
}
catch (...)
{
+ // nat 2016-08-18: not clear what exceptions the above COULD throw?!
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("for '" << username << "'"));
// we could have corrupt data, so simply return a null login param if so
LL_WARNS("AppInit") << "Invalid credential" << LL_ENDL;
}
diff --git a/indra/newview/llsecapi.h b/indra/newview/llsecapi.h
index 6fe3ee31cf..6af5a28fa5 100644
--- a/indra/newview/llsecapi.h
+++ b/indra/newview/llsecapi.h
@@ -32,6 +32,7 @@
#include <openssl/x509.h>
#include <ostream>
#include "llpointer.h"
+#include "llexception.h"
#ifdef LL_WINDOWS
#pragma warning(disable:4250)
@@ -116,17 +117,13 @@
-class LLProtectedDataException
+struct LLProtectedDataException: public LLException
{
-public:
- LLProtectedDataException(const char *msg)
+ LLProtectedDataException(const std::string& msg):
+ LLException(msg)
{
- LL_WARNS("SECAPI") << "Protected Data Error: " << (std::string)msg << LL_ENDL;
- mMsg = (std::string)msg;
+ LL_WARNS("SECAPI") << "Protected Data Error: " << msg << LL_ENDL;
}
- std::string getMessage() { return mMsg; }
-protected:
- std::string mMsg;
};
// class LLCertificate
@@ -334,22 +331,21 @@ std::ostream& operator <<(std::ostream& s, const LLCredential& cred);
// All error handling is via exceptions.
-class LLCertException
+class LLCertException: public LLException
{
public:
- LLCertException(LLPointer<LLCertificate> cert, const char* msg)
+ LLCertException(LLPointer<LLCertificate> cert, const std::string& msg):
+ LLException(msg)
{
mCert = cert;
- LL_WARNS("SECAPI") << "Certificate Error: " << (std::string)msg << LL_ENDL;
- mMsg = (std::string)msg;
+ LL_WARNS("SECAPI") << "Certificate Error: " << msg << LL_ENDL;
}
- LLPointer<LLCertificate> getCert() { return mCert; }
- std::string getMessage() { return mMsg; }
+ virtual ~LLCertException() throw() {}
+ LLPointer<LLCertificate> getCert() const { return mCert; }
protected:
LLPointer<LLCertificate> mCert;
- std::string mMsg;
};
class LLInvalidCertificate : public LLCertException
@@ -358,6 +354,7 @@ public:
LLInvalidCertificate(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalid")
{
}
+ virtual ~LLInvalidCertificate() throw() {}
protected:
};
@@ -367,6 +364,7 @@ public:
LLCertValidationTrustException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertUntrusted")
{
}
+ virtual ~LLCertValidationTrustException() throw() {}
protected:
};
@@ -378,7 +376,7 @@ public:
{
mHostname = hostname;
}
-
+ virtual ~LLCertValidationHostnameException() throw() {}
std::string getHostname() { return mHostname; }
protected:
std::string mHostname;
@@ -392,6 +390,7 @@ public:
{
mTime = current_time;
}
+ virtual ~LLCertValidationExpirationException() throw() {}
LLDate GetTime() { return mTime; }
protected:
LLDate mTime;
@@ -403,6 +402,7 @@ public:
LLCertKeyUsageValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertKeyUsage")
{
}
+ virtual ~LLCertKeyUsageValidationException() throw() {}
protected:
};
@@ -412,6 +412,7 @@ public:
LLCertBasicConstraintsValidationException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertBasicConstraints")
{
}
+ virtual ~LLCertBasicConstraintsValidationException() throw() {}
protected:
};
@@ -421,6 +422,7 @@ public:
LLCertValidationInvalidSignatureException(LLPointer<LLCertificate> cert) : LLCertException(cert, "CertInvalidSignature")
{
}
+ virtual ~LLCertValidationInvalidSignatureException() throw() {}
protected:
};
diff --git a/indra/newview/llsechandler_basic.cpp b/indra/newview/llsechandler_basic.cpp
index 40516f9bbb..d6fb801cc0 100644
--- a/indra/newview/llsechandler_basic.cpp
+++ b/indra/newview/llsechandler_basic.cpp
@@ -35,6 +35,8 @@
#include "llfile.h"
#include "lldir.h"
#include "llviewercontrol.h"
+#include "llexception.h"
+#include "stringize.h"
#include <vector>
#include <ios>
#include <openssl/ossl_typ.h>
@@ -72,14 +74,14 @@ LLBasicCertificate::LLBasicCertificate(const std::string& pem_cert)
if(pem_bio == NULL)
{
LL_WARNS("SECAPI") << "Could not allocate an openssl memory BIO." << LL_ENDL;
- throw LLInvalidCertificate(this);
+ LLTHROW(LLInvalidCertificate(this));
}
mCert = NULL;
PEM_read_bio_X509(pem_bio, &mCert, 0, NULL);
BIO_free(pem_bio);
if (!mCert)
{
- throw LLInvalidCertificate(this);
+ LLTHROW(LLInvalidCertificate(this));
}
}
@@ -88,7 +90,7 @@ LLBasicCertificate::LLBasicCertificate(X509* pCert)
{
if (!pCert || !pCert->cert_info)
{
- throw LLInvalidCertificate(this);
+ LLTHROW(LLInvalidCertificate(this));
}
mCert = X509_dup(pCert);
}
@@ -617,7 +619,7 @@ void LLBasicCertificateStore::load_from_file(const std::string& filename)
}
catch (...)
{
- LL_WARNS("SECAPI") << "Failure creating certificate from the certificate store file." << LL_ENDL;
+ LOG_UNHANDLED_EXCEPTION("creating certificate from the certificate store file");
}
X509_free(cert_x509);
cert_x509 = NULL;
@@ -873,22 +875,22 @@ void _validateCert(int validation_policy,
// check basic properties exist in the cert
if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info.has(CERT_SUBJECT_NAME_STRING))
{
- throw LLCertException(cert, "Cert doesn't have a Subject Name");
+ LLTHROW(LLCertException(cert, "Cert doesn't have a Subject Name"));
}
if(!current_cert_info.has(CERT_ISSUER_NAME_STRING))
{
- throw LLCertException(cert, "Cert doesn't have an Issuer Name");
+ LLTHROW(LLCertException(cert, "Cert doesn't have an Issuer Name"));
}
// check basic properties exist in the cert
if(!current_cert_info.has(CERT_VALID_FROM) || !current_cert_info.has(CERT_VALID_TO))
{
- throw LLCertException(cert, "Cert doesn't have an expiration period");
+ LLTHROW(LLCertException(cert, "Cert doesn't have an expiration period"));
}
if (!current_cert_info.has(CERT_SHA1_DIGEST))
{
- throw LLCertException(cert, "No SHA1 digest");
+ LLTHROW(LLCertException(cert, "No SHA1 digest"));
}
if (validation_policy & VALIDATION_POLICY_TIME)
@@ -903,7 +905,7 @@ void _validateCert(int validation_policy,
if((validation_date < current_cert_info[CERT_VALID_FROM].asDate()) ||
(validation_date > current_cert_info[CERT_VALID_TO].asDate()))
{
- throw LLCertValidationExpirationException(cert, validation_date);
+ LLTHROW(LLCertValidationExpirationException(cert, validation_date));
}
}
if (validation_policy & VALIDATION_POLICY_SSL_KU)
@@ -914,14 +916,14 @@ void _validateCert(int validation_policy,
!(_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE],
LLSD((std::string)CERT_KU_KEY_ENCIPHERMENT)))))
{
- throw LLCertKeyUsageValidationException(cert);
+ LLTHROW(LLCertKeyUsageValidationException(cert));
}
// only validate EKU if the cert has it
if(current_cert_info.has(CERT_EXTENDED_KEY_USAGE) && current_cert_info[CERT_EXTENDED_KEY_USAGE].isArray() &&
(!_LLSDArrayIncludesValue(current_cert_info[CERT_EXTENDED_KEY_USAGE],
LLSD((std::string)CERT_EKU_SERVER_AUTH))))
{
- throw LLCertKeyUsageValidationException(cert);
+ LLTHROW(LLCertKeyUsageValidationException(cert));
}
}
if (validation_policy & VALIDATION_POLICY_CA_KU)
@@ -930,7 +932,7 @@ void _validateCert(int validation_policy,
(!_LLSDArrayIncludesValue(current_cert_info[CERT_KEY_USAGE],
(std::string)CERT_KU_CERT_SIGN)))
{
- throw LLCertKeyUsageValidationException(cert);
+ LLTHROW(LLCertKeyUsageValidationException(cert));
}
}
@@ -942,13 +944,13 @@ void _validateCert(int validation_policy,
if(!current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_CA) ||
!current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_CA])
{
- throw LLCertBasicConstraintsValidationException(cert);
+ LLTHROW(LLCertBasicConstraintsValidationException(cert));
}
if (current_cert_info[CERT_BASIC_CONSTRAINTS].has(CERT_BASIC_CONSTRAINTS_PATHLEN) &&
((current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger() != 0) &&
(depth > current_cert_info[CERT_BASIC_CONSTRAINTS][CERT_BASIC_CONSTRAINTS_PATHLEN].asInteger())))
{
- throw LLCertBasicConstraintsValidationException(cert);
+ LLTHROW(LLCertBasicConstraintsValidationException(cert));
}
}
}
@@ -1018,7 +1020,7 @@ void LLBasicCertificateStore::validate(int validation_policy,
if(cert_chain->size() < 1)
{
- throw LLCertException(NULL, "No certs in chain");
+ LLTHROW(LLCertException(NULL, "No certs in chain"));
}
iterator current_cert = cert_chain->begin();
LLSD current_cert_info;
@@ -1033,11 +1035,11 @@ void LLBasicCertificateStore::validate(int validation_policy,
(*current_cert)->getLLSD(current_cert_info);
if(!validation_params.has(CERT_HOSTNAME))
{
- throw LLCertException((*current_cert), "No hostname passed in for validation");
+ LLTHROW(LLCertException((*current_cert), "No hostname passed in for validation"));
}
if(!current_cert_info.has(CERT_SUBJECT_NAME) || !current_cert_info[CERT_SUBJECT_NAME].has(CERT_NAME_CN))
{
- throw LLInvalidCertificate((*current_cert));
+ LLTHROW(LLInvalidCertificate((*current_cert)));
}
LL_DEBUGS("SECAPI") << "Validating the hostname " << validation_params[CERT_HOSTNAME].asString() <<
@@ -1054,7 +1056,7 @@ void LLBasicCertificateStore::validate(int validation_policy,
X509* cert_x509 = (*current_cert)->getOpenSSLX509();
if(!cert_x509)
{
- throw LLInvalidCertificate((*current_cert));
+ LLTHROW(LLInvalidCertificate((*current_cert)));
}
std::string sha1_hash((const char *)cert_x509->sha1_hash, SHA_DIGEST_LENGTH);
X509_free( cert_x509 );
@@ -1075,7 +1077,7 @@ void LLBasicCertificateStore::validate(int validation_policy,
if((validation_date < cache_entry->second.first) ||
(validation_date > cache_entry->second.second))
{
- throw LLCertValidationExpirationException((*current_cert), validation_date);
+ LLTHROW(LLCertValidationExpirationException((*current_cert), validation_date));
}
}
// successfully found in cache
@@ -1107,7 +1109,7 @@ void LLBasicCertificateStore::validate(int validation_policy,
if(!_verify_signature((*current_cert),
previous_cert))
{
- throw LLCertValidationInvalidSignatureException(previous_cert);
+ LLTHROW(LLCertValidationInvalidSignatureException(previous_cert));
}
}
_validateCert(local_validation_policy,
@@ -1156,7 +1158,7 @@ void LLBasicCertificateStore::validate(int validation_policy,
if(!_verify_signature((*found_store_cert),
(*current_cert)))
{
- throw LLCertValidationInvalidSignatureException(*current_cert);
+ LLTHROW(LLCertValidationInvalidSignatureException(*current_cert));
}
// successfully validated.
mTrustedCertCache[sha1_hash] = std::pair<LLDate, LLDate>(from_time, to_time);
@@ -1173,7 +1175,7 @@ void LLBasicCertificateStore::validate(int validation_policy,
if (validation_policy & VALIDATION_POLICY_TRUSTED)
{
// we reached the end without finding a trusted cert.
- throw LLCertValidationTrustException((*cert_chain)[cert_chain->size()-1]);
+ LLTHROW(LLCertValidationTrustException((*cert_chain)[cert_chain->size()-1]));
}
mTrustedCertCache[sha1_hash] = std::pair<LLDate, LLDate>(from_time, to_time);
@@ -1261,7 +1263,7 @@ void LLSecAPIBasicHandler::_readProtectedData()
protected_data_stream.read((char *)salt, STORE_SALT_SIZE);
if (protected_data_stream.gcount() < STORE_SALT_SIZE)
{
- throw LLProtectedDataException("Config file too short.");
+ LLTHROW(LLProtectedDataException("Config file too short."));
}
cipher.decrypt(salt, STORE_SALT_SIZE);
@@ -1301,7 +1303,7 @@ void LLSecAPIBasicHandler::_readProtectedData()
if (parser->parse(parse_stream, mProtectedDataMap,
LLSDSerialize::SIZE_UNLIMITED) == LLSDParser::PARSE_FAILURE)
{
- throw LLProtectedDataException("Config file cannot be decrypted.");
+ LLTHROW(LLProtectedDataException("Config file cannot be decrypted."));
}
}
}
@@ -1364,7 +1366,7 @@ void LLSecAPIBasicHandler::_writeProtectedData()
}
catch (...)
{
- LL_WARNS() << "LLProtectedDataException(Error writing Protected Data Store)" << LL_ENDL;
+ LOG_UNHANDLED_EXCEPTION("LLProtectedDataException(Error writing Protected Data Store)");
// it's good practice to clean up any secure information on error
// (even though this file isn't really secure. Perhaps in the future
// it may be, however.
@@ -1372,39 +1374,39 @@ void LLSecAPIBasicHandler::_writeProtectedData()
// EXP-1825 crash in LLSecAPIBasicHandler::_writeProtectedData()
// Decided throwing an exception here was overkill until we figure out why this happens
- //throw LLProtectedDataException("Error writing Protected Data Store");
+ //LLTHROW(LLProtectedDataException("Error writing Protected Data Store"));
}
- try
- {
- // move the temporary file to the specified file location.
- if((( (LLFile::isfile(mProtectedDataFilename) != 0)
- && (LLFile::remove(mProtectedDataFilename) != 0)))
- || (LLFile::rename(tmp_filename, mProtectedDataFilename)))
- {
- LL_WARNS() << "LLProtectedDataException(Could not overwrite protected data store)" << LL_ENDL;
- LLFile::remove(tmp_filename);
+ try
+ {
+ // move the temporary file to the specified file location.
+ if((( (LLFile::isfile(mProtectedDataFilename) != 0)
+ && (LLFile::remove(mProtectedDataFilename) != 0)))
+ || (LLFile::rename(tmp_filename, mProtectedDataFilename)))
+ {
+ LL_WARNS() << "LLProtectedDataException(Could not overwrite protected data store)" << LL_ENDL;
+ LLFile::remove(tmp_filename);
- // EXP-1825 crash in LLSecAPIBasicHandler::_writeProtectedData()
- // Decided throwing an exception here was overkill until we figure out why this happens
- //throw LLProtectedDataException("Could not overwrite protected data store");
- }
+ // EXP-1825 crash in LLSecAPIBasicHandler::_writeProtectedData()
+ // Decided throwing an exception here was overkill until we figure out why this happens
+ //LLTHROW(LLProtectedDataException("Could not overwrite protected data store"));
+ }
}
catch (...)
{
- LL_WARNS() << "LLProtectedDataException(Error renaming '" << tmp_filename
- << "' to '" << mProtectedDataFilename << "')" << LL_ENDL;
+ LOG_UNHANDLED_EXCEPTION(STRINGIZE("renaming '" << tmp_filename << "' to '"
+ << mProtectedDataFilename << "'"));
// it's good practice to clean up any secure information on error
// (even though this file isn't really secure. Perhaps in the future
- // it may be, however.
+ // it may be, however).
LLFile::remove(tmp_filename);
//crash in LLSecAPIBasicHandler::_writeProtectedData()
// Decided throwing an exception here was overkill until we figure out why this happens
- //throw LLProtectedDataException("Error writing Protected Data Store");
+ //LLTHROW(LLProtectedDataException("Error writing Protected Data Store"));
}
}
-
+
// instantiate a certificate from a pem string
LLPointer<LLCertificate> LLSecAPIBasicHandler::getCertificate(const std::string& pem_cert)
{
diff --git a/indra/test/llapp_tut.cpp b/indra/test/llapp_tut.cpp
index aa5c0672e6..98e714a497 100644
--- a/indra/test/llapp_tut.cpp
+++ b/indra/test/llapp_tut.cpp
@@ -41,7 +41,7 @@ namespace tut
public:
virtual bool init() { return true; }
virtual bool cleanup() { return true; }
- virtual bool mainLoop() { return true; }
+ virtual bool frame() { return true; }
};
LLTestApp* mApp;
application()
diff --git a/indra/viewer_components/login/lllogin.cpp b/indra/viewer_components/login/lllogin.cpp
index 53d4acc9e0..c767d52c7b 100644
--- a/indra/viewer_components/login/lllogin.cpp
+++ b/indra/viewer_components/login/lllogin.cpp
@@ -42,6 +42,8 @@
#include "llevents.h"
#include "lleventfilter.h"
#include "lleventcoro.h"
+#include "llexception.h"
+#include "stringize.h"
//*********************
// LLLogin
@@ -128,30 +130,23 @@ void LLLogin::Impl::connect(const std::string& uri, const LLSD& login_params)
void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
{
- try
- {
- LLSD printable_params = login_params;
- //if(printable_params.has("params")
- // && printable_params["params"].has("passwd"))
- //{
- // printable_params["params"]["passwd"] = "*******";
- //}
- LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName()
+ LLSD printable_params = login_params;
+ if (printable_params.has("params")
+ && printable_params["params"].has("passwd"))
+ {
+ printable_params["params"]["passwd"] = "*******";
+ }
+ try
+ {
+ LL_DEBUGS("LLLogin") << "Entering coroutine " << LLCoros::instance().getName()
<< " with uri '" << uri << "', parameters " << printable_params << LL_ENDL;
- // Arriving in SRVRequest state
- LLEventStream replyPump("SRVreply", true);
- // Should be an array of one or more uri strings.
-
LLEventPump& xmlrpcPump(LLEventPumps::instance().obtain("LLXMLRPCTransaction"));
// EXT-4193: use a DIFFERENT reply pump than for the SRV request. We used
// to share them -- but the EXT-3934 fix made it possible for an abandoned
// SRV response to arrive just as we were expecting the XMLRPC response.
LLEventStream loginReplyPump("loginreply", true);
- // Loop through the rewrittenURIs, counting attempts along the way.
- // Because of possible redirect responses, we may make more than one
- // attempt per rewrittenURIs entry.
LLSD::Integer attempts = 0;
LLSD request(login_params);
@@ -167,11 +162,11 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
LLSD progress_data;
progress_data["attempt"] = attempts;
progress_data["request"] = request;
- if(progress_data["request"].has("params")
- && progress_data["request"]["params"].has("passwd"))
- {
- progress_data["request"]["params"]["passwd"] = "*******";
- }
+ if (progress_data["request"].has("params")
+ && progress_data["request"]["params"].has("passwd"))
+ {
+ progress_data["request"]["params"]["passwd"] = "*******";
+ }
sendProgressEvent("offline", "authenticating", progress_data);
// We expect zero or more "Downloading" status events, followed by
@@ -189,8 +184,8 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
// Still Downloading -- send progress update.
sendProgressEvent("offline", "downloading");
}
-
- LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL;
+
+ LL_DEBUGS("LLLogin") << "Auth Response: " << mAuthResponse << LL_ENDL;
status = mAuthResponse["status"].asString();
// Okay, we've received our final status event for this
@@ -202,7 +197,7 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
break;
}
- sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]);
+ sendProgressEvent("offline", "indeterminate", mAuthResponse["responses"]);
// Here the login service at the current URI is redirecting us
// to some other URI ("indeterminate" -- why not "redirect"?).
@@ -212,8 +207,7 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
request["method"] = mAuthResponse["responses"]["next_method"].asString();
} // loop back to try the redirected URI
- // Here we're done with redirects for the current rewrittenURIs
- // entry.
+ // Here we're done with redirects.
if (status == "Complete")
{
// StatusComplete does not imply auth success. Check the
@@ -230,14 +224,14 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
return; // Done!
}
-// /* Sometimes we end with "Started" here. Slightly slow server?
-// * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below.
-// */
-// if( status == "Started")
-// {
-// LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL;
-// continue;
-// }
+// /* Sometimes we end with "Started" here. Slightly slow server?
+// * Seems to be ok to just skip it. Otherwise we'd error out and crash in the if below.
+// */
+// if( status == "Started")
+// {
+// LL_DEBUGS("LLLogin") << mAuthResponse << LL_ENDL;
+// continue;
+// }
// If we don't recognize status at all, trouble
if (! (status == "CURLError"
@@ -250,27 +244,25 @@ void LLLogin::Impl::loginCoro(std::string uri, LLSD login_params)
}
// Here status IS one of the errors tested above.
-
- // Here we got through all the rewrittenURIs without succeeding. Tell
- // caller this didn't work out so well. Of course, the only failure data
- // we can reasonably show are from the last of the rewrittenURIs.
-
- // *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an
- // llsd with no "responses" node. To make the output from an incomplete login symmetrical
- // to success, add a data/message and data/reason fields.
- LLSD error_response;
- error_response["reason"] = mAuthResponse["status"];
- error_response["errorcode"] = mAuthResponse["errorcode"];
- error_response["message"] = mAuthResponse["error"];
- if(mAuthResponse.has("certificate"))
- {
- error_response["certificate"] = mAuthResponse["certificate"];
- }
- sendProgressEvent("offline", "fail.login", error_response);
- }
- catch (...) {
- LL_ERRS() << "login exception caught" << LL_ENDL;
- }
+ // Tell caller this didn't work out so well.
+
+ // *NOTE: The response from LLXMLRPCListener's Poller::poll method returns an
+ // llsd with no "responses" node. To make the output from an incomplete login symmetrical
+ // to success, add a data/message and data/reason fields.
+ LLSD error_response;
+ error_response["reason"] = mAuthResponse["status"];
+ error_response["errorcode"] = mAuthResponse["errorcode"];
+ error_response["message"] = mAuthResponse["error"];
+ if(mAuthResponse.has("certificate"))
+ {
+ error_response["certificate"] = mAuthResponse["certificate"];
+ }
+ sendProgressEvent("offline", "fail.login", error_response);
+ }
+ catch (...) {
+ CRASH_ON_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << LLCoros::instance().getName()
+ << "('" << uri << "', " << printable_params << ")"));
+ }
}
void LLLogin::Impl::disconnect()
diff --git a/indra/viewer_components/updater/llupdatedownloader.cpp b/indra/viewer_components/updater/llupdatedownloader.cpp
index 382689afa0..04e0395c50 100644
--- a/indra/viewer_components/updater/llupdatedownloader.cpp
+++ b/indra/viewer_components/updater/llupdatedownloader.cpp
@@ -27,7 +27,7 @@
#include "llupdatedownloader.h"
#include "httpcommon.h"
-#include <stdexcept>
+#include "llexception.h"
#include <boost/format.hpp>
#include <boost/lexical_cast.hpp>
#include <curl/curl.h>
@@ -85,11 +85,11 @@ private:
namespace {
class DownloadError:
- public std::runtime_error
+ public LLException
{
public:
DownloadError(const char * message):
- std::runtime_error(message)
+ LLException(message)
{
; // No op.
}
@@ -467,7 +467,7 @@ void LLUpdateDownloader::Implementation::initializeCurlGet(std::string const & u
if(!mCurl)
{
- throw DownloadError("failed to initialize curl");
+ LLTHROW(DownloadError("failed to initialize curl"));
}
throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_NOSIGNAL, true));
throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_FOLLOWLOCATION, true));
@@ -508,7 +508,7 @@ void LLUpdateDownloader::Implementation::resumeDownloading(size_t startByte)
mHeaderList = curl_slist_append(mHeaderList, rangeHeaderFormat.str().c_str());
if(mHeaderList == 0)
{
- throw DownloadError("cannot add Range header");
+ LLTHROW(DownloadError("cannot add Range header"));
}
throwOnCurlError(curl_easy_setopt(mCurl.get(), CURLOPT_HTTPHEADER, mHeaderList));
@@ -524,7 +524,7 @@ void LLUpdateDownloader::Implementation::startDownloading(LLURI const & uri, std
mDownloadData["hash"] = hash;
mDownloadData["current_version"] = ll_get_version();
LLSD path = uri.pathArray();
- if(path.size() == 0) throw DownloadError("no file path");
+ if(path.size() == 0) LLTHROW(DownloadError("no file path"));
std::string fileName = path[path.size() - 1].asString();
std::string filePath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, fileName);
mDownloadData["path"] = filePath;
@@ -547,9 +547,9 @@ void LLUpdateDownloader::Implementation::throwOnCurlError(CURLcode code)
if(code != CURLE_OK) {
const char * errorString = curl_easy_strerror(code);
if(errorString != 0) {
- throw DownloadError(curl_easy_strerror(code));
+ LLTHROW(DownloadError(curl_easy_strerror(code)));
} else {
- throw DownloadError("unknown curl error");
+ LLTHROW(DownloadError("unknown curl error"));
}
} else {
; // No op.
diff --git a/indra/viewer_components/updater/llupdateinstaller.cpp b/indra/viewer_components/updater/llupdateinstaller.cpp
index a0e2c0b362..1c7629da23 100644
--- a/indra/viewer_components/updater/llupdateinstaller.cpp
+++ b/indra/viewer_components/updater/llupdateinstaller.cpp
@@ -30,23 +30,25 @@
#include "llupdateinstaller.h"
#include "lldir.h"
#include "llsd.h"
+#include "llexception.h"
#if defined(LL_WINDOWS)
#pragma warning(disable: 4702) // disable 'unreachable code' so we can use lexical_cast (really!).
#endif
#include <boost/lexical_cast.hpp>
-
namespace {
- class RelocateError {};
-
-
+ struct RelocateError: public LLException
+ {
+ RelocateError(): LLException("llupdateinstaller: RelocateError") {}
+ };
+
std::string copy_to_temp(std::string const & path)
{
std::string scriptFile = gDirUtilp->getBaseFileName(path);
std::string newPath = gDirUtilp->getExpandedFilename(LL_PATH_TEMP, scriptFile);
apr_status_t status = apr_file_copy(path.c_str(), newPath.c_str(), APR_FILE_SOURCE_PERMS, gAPRPoolp);
- if(status != APR_SUCCESS) throw RelocateError();
+ if(status != APR_SUCCESS) LLTHROW(RelocateError());
return newPath;
}
diff --git a/indra/viewer_components/updater/llupdaterservice.cpp b/indra/viewer_components/updater/llupdaterservice.cpp
index 788955a1b2..1665e41e70 100644
--- a/indra/viewer_components/updater/llupdaterservice.cpp
+++ b/indra/viewer_components/updater/llupdaterservice.cpp
@@ -32,6 +32,7 @@
#include "lltimer.h"
#include "llupdatechecker.h"
#include "llupdateinstaller.h"
+#include "llexception.h"
#include <boost/scoped_ptr.hpp>
#include <boost/weak_ptr.hpp>
@@ -190,8 +191,8 @@ void LLUpdaterServiceImpl::initialize(const std::string& channel,
{
if(mIsChecking || mIsDownloading)
{
- throw LLUpdaterService::UsageError("LLUpdaterService::initialize call "
- "while updater is running.");
+ LLTHROW(LLUpdaterService::UsageError("LLUpdaterService::initialize call "
+ "while updater is running."));
}
mChannel = channel;
@@ -222,8 +223,8 @@ void LLUpdaterServiceImpl::startChecking(bool install_if_ready)
{
if(mChannel.empty() || mVersion.empty())
{
- throw LLUpdaterService::UsageError("Set params before call to "
- "LLUpdaterService::startCheck().");
+ LLTHROW(LLUpdaterService::UsageError("Set params before call to "
+ "LLUpdaterService::startCheck()."));
}
mIsChecking = true;
diff --git a/indra/viewer_components/updater/llupdaterservice.h b/indra/viewer_components/updater/llupdaterservice.h
index 95bbe1695c..78e8c6b290 100644
--- a/indra/viewer_components/updater/llupdaterservice.h
+++ b/indra/viewer_components/updater/llupdaterservice.h
@@ -29,16 +29,17 @@
#include <boost/shared_ptr.hpp>
#include <boost/function.hpp>
#include "llhasheduniqueid.h"
+#include "llexception.h"
class LLUpdaterServiceImpl;
class LLUpdaterService
{
public:
- class UsageError: public std::runtime_error
+ class UsageError: public LLException
{
public:
- UsageError(const std::string& msg) : std::runtime_error(msg) {}
+ UsageError(const std::string& msg) : LLException(msg) {}
};
// Name of the event pump through which update events will be delivered.
diff --git a/indra/win_crash_logger/llcrashloggerwindows.cpp b/indra/win_crash_logger/llcrashloggerwindows.cpp
index 23c29e6b18..167acf6ac7 100644
--- a/indra/win_crash_logger/llcrashloggerwindows.cpp
+++ b/indra/win_crash_logger/llcrashloggerwindows.cpp
@@ -454,7 +454,7 @@ void LLCrashLoggerWindows::gatherPlatformSpecificFiles()
//mDebugLog["DisplayDeviceInfo"] = gDXHardware.getDisplayInfo(); //Not initialized.
}
-bool LLCrashLoggerWindows::mainLoop()
+bool LLCrashLoggerWindows::frame()
{
LL_INFOS() << "CrashSubmitBehavior is " << mCrashBehavior << LL_ENDL;
@@ -503,14 +503,14 @@ bool LLCrashLoggerWindows::mainLoop()
TranslateMessage(&msg);
DispatchMessage(&msg);
}
- return msg.wParam;
+ return true; // msg.wParam;
}
else
{
LL_WARNS() << "Unknown crash behavior " << mCrashBehavior << LL_ENDL;
- return 1;
+ return true; // 1;
}
- return 0;
+ return true; // 0;
}
void LLCrashLoggerWindows::updateApplication(const std::string& message)
diff --git a/indra/win_crash_logger/llcrashloggerwindows.h b/indra/win_crash_logger/llcrashloggerwindows.h
index 1812e2737e..f89b8708dc 100644
--- a/indra/win_crash_logger/llcrashloggerwindows.h
+++ b/indra/win_crash_logger/llcrashloggerwindows.h
@@ -46,7 +46,7 @@ public:
static LLCrashLoggerWindows* sInstance;
virtual bool init();
- virtual bool mainLoop();
+ virtual bool frame();
virtual void updateApplication(const std::string& message = LLStringUtil::null);
virtual bool cleanup();
virtual void gatherPlatformSpecificFiles();
diff --git a/indra/win_crash_logger/win_crash_logger.cpp b/indra/win_crash_logger/win_crash_logger.cpp
index 366edd894b..7466dbb766 100644
--- a/indra/win_crash_logger/win_crash_logger.cpp
+++ b/indra/win_crash_logger/win_crash_logger.cpp
@@ -52,7 +52,7 @@ int APIENTRY WinMain(HINSTANCE hInstance,
}
app.processingLoop();
- app.mainLoop();
+ app.frame();
app.cleanup();
LL_INFOS() << "Crash reporter finished normally." << LL_ENDL;
return 0;