From 464a0df4c1d3e4073fe0bde506ac1d4aa194b02f Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 30 Jun 2016 16:51:50 -0400 Subject: DRTVWR-418: Unify control flow through LLAppViewer across platforms. The LLApp API used to consist of init(), mainLoop(), cleanup() methods. This makes sense -- but on Mac that structure was being subverted. The method called mainLoop() was in fact being called once per frame. There was initialization code in the method, which (on Mac) needed to be skipped with an already-initialized bool. There was a 'while' loop which (on Mac) needed to be turned into an 'if' instead so the method would return after every frame. Rename LLApp::mainLoop() to frame(). Propagate through subclasses LLAppViewer and LLCrashLogger. Document the fact that frame() returns true to mean "done." (This was always the case, but had to be inferred from the code.) Rename the Mac Objective-C function mainLoop to oneFrame. Rename the C++ free function it calls from runMainLoop() to pumpMainLoop(). Add comments to llappdelegate-objc.mm explaining (inferred) control flow. Change the Linux viewer main() and the Windows viewer WINMAIN() from a single LLAppViewer::mainLoop() call to repeatedly call frame() until it returns true. Move initialization code from the top of LLAppViewer::frame() to the init() method, where it more properly belongs. Remove corresponding mMainLoopInitialized flag (and all references) from LLAppViewer. Remove 'while (! LLApp::isExiting())' (or on Mac, 'if (! LLApp::isExiting())') from LLAppViewer::frame() -- thus unindenting the whole body of the 'while' and causing many lines of apparent change. (Apologies to reviewers.) There are four LLApp states: APP_STATUS_RUNNING, APP_STATUS_QUITTING, APP_STATUS_STOPPED and APP_STATUS_ERROR. Change LLAppViewer::frame() return value from (isExiting()) (QUITTING or ERROR) to (! isRunning()). I do not know under what circumstances the state might transition to STOPPED during a frame() call, but I'm quite sure that if it does, we don't want to call frame() again. We only want a subsequent call if the state is RUNNING. Also rename mainLoop() method in LLCrashLogger subclasses LLCrashLoggerWindows, LLCrashLoggerMac, LLCrashLoggerLinux. Of course it's completely up to the frame() method whether to yield control; none of those in fact do. Honor protocol by returning true (frame() is done), even though each one's main() caller ignores the return value. In fact LLCrashLoggerWindows::mainLoop() wasn't using the return protocol correctly anyway, returning wParam or 0 or 1 -- possibly because the return protocol was never explicitly documented. It should always return true: "I'm done, don't call me again." --- indra/llcommon/llapp.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'indra/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 -- cgit v1.2.3 From 9c49a6c91dd9b5bbe811fcd91d8992ed6bac33e7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 19 Jul 2016 16:25:25 -0400 Subject: MAINT-5011: Introduce LLException base class for viewer exceptions. This also introduces LLContinueError for exceptions which should interrupt some part of viewer processing (e.g. the current coroutine) but should attempt to let the viewer session proceed. Derive all existing viewer exception classes from LLException rather than from std::runtime_error or std::logic_error. Use BOOST_THROW_EXCEPTION() rather than plain 'throw' to enrich the thrown exception with source file, line number and containing function. --- indra/llcommon/CMakeLists.txt | 1 + indra/llcommon/lldependencies.cpp | 3 +- indra/llcommon/lldependencies.h | 6 ++-- indra/llcommon/lleventcoro.cpp | 3 +- indra/llcommon/lleventcoro.h | 6 ++-- indra/llcommon/llevents.cpp | 17 ++++++---- indra/llcommon/llevents.h | 14 ++++---- indra/llcommon/llexception.h | 63 ++++++++++++++++++++++++++++++++++++ indra/llcommon/llleap.cpp | 5 +-- indra/llcommon/llleap.h | 6 ++-- indra/llcommon/llprocess.cpp | 23 +++++++------ indra/llcommon/llprocess.h | 6 ++-- indra/llcommon/llthreadsafequeue.cpp | 13 ++++---- indra/llcommon/llthreadsafequeue.h | 7 ++-- indra/llcommon/lluuid.cpp | 2 +- indra/llcommon/tests/wrapllerrs.h | 9 +++--- 16 files changed, 129 insertions(+), 55 deletions(-) create mode 100644 indra/llcommon/llexception.h (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 907dbab8f8..44f45144e5 100644 --- a/indra/llcommon/CMakeLists.txt +++ b/indra/llcommon/CMakeLists.txt @@ -157,6 +157,7 @@ set(llcommon_HEADER_FILES lleventfilter.h llevents.h lleventemitter.h + llexception.h llfasttimer.h llfile.h llfindlocale.h diff --git a/indra/llcommon/lldependencies.cpp b/indra/llcommon/lldependencies.cpp index 0e72c175cb..87a699ff14 100644 --- a/indra/llcommon/lldependencies.cpp +++ b/indra/llcommon/lldependencies.cpp @@ -39,6 +39,7 @@ #include #include #include +#include // other Linden headers 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()); + BOOST_THROW_EXCEPTION(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 #include #include -#include #include #include #include #include #include #include +#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/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 2d5f964deb..f444530a17 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -34,6 +34,7 @@ #include // std headers // external library headers +#include // other Linden headers #include "llsdserialize.h" #include "llerror.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); + BOOST_THROW_EXCEPTION(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 #include -#include #include // 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..50919edb8e 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -45,6 +45,7 @@ #include // external library headers #include +#include #if LL_WINDOWS #pragma warning (push) #pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no @@ -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 + "'"); + BOOST_THROW_EXCEPTION(LLEventPump::DupPumpName(std::string("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,9 @@ 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() + "'"); + BOOST_THROW_EXCEPTION( + DupListenerName(std::string("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 +355,9 @@ 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()); + BOOST_THROW_EXCEPTION( + Cycle(std::string("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 +421,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()); + BOOST_THROW_EXCEPTION(OrderChange(out.str())); } // This node becomes the previous one. previous = dmi->second; @@ -608,7 +611,7 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const { if (! mListener) { - throw Empty("attempting to call uninitialized"); + BOOST_THROW_EXCEPTION(Empty("attempting to call uninitialized")); } return (*mListener)(event); } diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index ba4fcd766e..8ff337911d 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -37,7 +37,6 @@ #include #include #include -#include #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 @@ -188,10 +188,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 +371,10 @@ public: * you didn't pass tweak=true 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 +399,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.h b/indra/llcommon/llexception.h new file mode 100644 index 0000000000..3ac2f4762f --- /dev/null +++ b/indra/llcommon/llexception.h @@ -0,0 +1,63 @@ +/** + * @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 +#include + +/** + * LLException is intended as the common base class from which all + * viewer-specific exceptions are derived. It is itself a subclass of + * boost::exception; use catch (const boost::exception& e) clause to log the + * string from boost::diagnostic_information(e). + * + * Since it is also derived from std::exception, a generic catch (const + * std::exception&) should also work, though what() is unlikely to be as + * informative as boost::diagnostic_information(). + * + * Please use BOOST_THROW_EXCEPTION() + * http://www.boost.org/doc/libs/release/libs/exception/doc/BOOST_THROW_EXCEPTION.html + * to throw viewer exceptions whenever possible. This enriches the exception's + * diagnostic_information() with the source file, line and containing function + * of the BOOST_THROW_EXCEPTION() macro. + * + * 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) + {} +}; + +#endif /* ! defined(LL_LLEXCEPTION_H) */ diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index 84d2a12f65..a8bb9bc53a 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -21,6 +21,7 @@ #include #include #include +#include // other Linden headers #include "llerror.h" #include "llstring.h" @@ -69,7 +70,7 @@ public: // Rule out empty vector if (plugin.empty()) { - throw Error("no plugin command"); + BOOST_THROW_EXCEPTION(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)); + BOOST_THROW_EXCEPTION(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 #include -#include /** * 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..ca19c94736 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -39,6 +39,7 @@ #include #include #include +#include #include #include #include @@ -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,9 @@ LLProcess::LLProcess(const LLSDOrParams& params): if (! params.validateBlock(true)) { - throw LLProcessError(STRINGIZE("not launched: failed parameter validation\n" - << LLSDNotationStreamer(params))); + BOOST_THROW_EXCEPTION( + LLProcessError(STRINGIZE("not launched: failed parameter validation\n" + << LLSDNotationStreamer(params)))); } mPostend = params.postend; @@ -596,10 +598,11 @@ LLProcess::LLProcess(const LLSDOrParams& params): } else { - throw LLProcessError(STRINGIZE("For " << params.executable() - << ": unsupported FileParam for " << which - << ": type='" << fparam.type() - << "', name='" << fparam.name() << "'")); + BOOST_THROW_EXCEPTION( + 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 +681,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")); + BOOST_THROW_EXCEPTION(LLProcessError(STRINGIZE(params << " failed"))); } // arrange to call status_callback() @@ -1063,7 +1066,7 @@ PIPETYPE& LLProcess::getPipe(FILESLOT slot) PIPETYPE* wp = getPipePtr(error, slot); if (! wp) { - throw NoPipe(error); + BOOST_THROW_EXCEPTION(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 #include #include #include #include // std::ostream -#include #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..a004618e96 100644 --- a/indra/llcommon/llthreadsafequeue.cpp +++ b/indra/llcommon/llthreadsafequeue.cpp @@ -26,6 +26,7 @@ #include "linden_common.h" #include #include +#include #include "llthreadsafequeue.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) BOOST_THROW_EXCEPTION(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) BOOST_THROW_EXCEPTION(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(); + BOOST_THROW_EXCEPTION(LLThreadSafeQueueInterrupt()); } else if(status != APR_SUCCESS) { - throw LLThreadSafeQueueError("push failed"); + BOOST_THROW_EXCEPTION(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(); + BOOST_THROW_EXCEPTION(LLThreadSafeQueueInterrupt()); } else if(status != APR_SUCCESS) { - throw LLThreadSafeQueueError("pop failed"); + BOOST_THROW_EXCEPTION(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 -#include 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..785cf47926 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 ) BOOST_THROW_EXCEPTION(bad_input_data()); ret = ret * 85 + (s-encodeTable); } return ret; diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index 785197ba11..fa16fd6915 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -35,13 +35,14 @@ #include #include "llerrorcontrol.h" +#include "llexception.h" #include "stringize.h" #include #include #include +#include #include #include -#include // statically reference the function in test.cpp... it's short, we could // replicate, but better to reuse @@ -67,9 +68,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 +79,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); + BOOST_THROW_EXCEPTION(FatalException(message)); } std::string error; -- cgit v1.2.3 From 0b4b1219459d123d62d4b6e332781416e73e6666 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 20 Jul 2016 14:56:49 -0700 Subject: MAINT-6570: Changes for LLCheckedHandle plus some timeout issues from AndreyK regarding script compiles/resets. --- indra/llcommon/llhandle.h | 85 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index 401e4d759a..dfbfb91fc1 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -213,4 +213,89 @@ private: mutable LLRootHandle mHandle; }; +/* + * $TODO: Derive from LLException + */ +struct LLExeceptionStaleHandle : public std::runtime_error +{ +public: + LLExeceptionStaleHandle(): + std::runtime_error("Attempt to access stale handle.") + {} +}; + +/** + * This is a simple wrapper for Handles, allowing direct calls to the underlying + * pointer. The checked handle will throw a LLExeceptionStaleHandle if an attempt + * is made to access the object referenced by the handle and that object has + * been destroyed. + **/ +template +class LLCheckedHandle: private boost::noncopyable +{ +public: + LLCheckedHandle(LLHandle handle): + mHandle(handle) + { } + + /** + * Retrieve the underlying pointer by resolving the handle. If the handle + * returns NULL for the pointer throw an LLExeceptionStaleHandle exception. + */ + T* get() const + { + T* ptr = mHandle.get(); + if (!ptr) + BOOST_THROW_EXCEPTION(LLExeceptionStaleHandle()); + return ptr; + } + + /** + * Test the handle to see if it is still valid. Returns true if it is, + * false if it is not. Does not trow. + */ + bool test() const + { + return (mHandle.get() != NULL); + } + + /** + * Test the underlying handle. If it is no longer valid, throw a LLExeceptionStaleHandle. + */ + void check() const + { + get(); + } + + /** + * Get the contained handle. + */ + LLHandle getHandle() const + { + return mHandle; + } + + /** + * Converts the LLCheckedHandle to a bool. Allows for if (chkdHandle) {} + * Does not throw. + */ + operator bool() const + { + return test(); + } + + /** + * Attempt to call a method or access a member in the structure referenced + * by the handle. If the handle no longer points to a valid structure + * throw a LLExeceptionStaleHandle. + */ + T* operator ->() const + { + return get(); + } + +private: + LLHandle mHandle; +}; + #endif -- cgit v1.2.3 From d0d07ccac565632497c50510714b30503be8aa94 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Wed, 20 Jul 2016 16:45:17 -0700 Subject: Fix for linux build --- indra/llcommon/llhandle.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index dfbfb91fc1..98ad7af81a 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -28,8 +28,10 @@ #define LLHANDLE_H #include "llpointer.h" +#include #include #include +#include /** * Helper object for LLHandle. Don't instantiate these directly, used @@ -216,7 +218,7 @@ private: /* * $TODO: Derive from LLException */ -struct LLExeceptionStaleHandle : public std::runtime_error +class LLExeceptionStaleHandle : public std::runtime_error { public: LLExeceptionStaleHandle(): -- cgit v1.2.3 From de438e7512de6dacafee592b89c22c0f062062b6 Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Thu, 21 Jul 2016 11:35:24 -0700 Subject: MAINT-6570: Feedback from code review. --- indra/llcommon/llhandle.h | 68 +++++++++++++++++++++-------------------------- 1 file changed, 31 insertions(+), 37 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index 98ad7af81a..8e073aab27 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -28,6 +28,7 @@ #define LLHANDLE_H #include "llpointer.h" +#include "llexception.h" #include #include #include @@ -215,64 +216,53 @@ private: mutable LLRootHandle mHandle; }; -/* - * $TODO: Derive from LLException - */ -class LLExeceptionStaleHandle : public std::runtime_error + + +class LLCheckedHandleBase { public: - LLExeceptionStaleHandle(): - std::runtime_error("Attempt to access stale handle.") - {} + class Stale : public LLException + { + public: + Stale() : + LLException("Attempt to access stale handle.") + {} + }; + +protected: + LLCheckedHandleBase() { } + }; /** * This is a simple wrapper for Handles, allowing direct calls to the underlying - * pointer. The checked handle will throw a LLExeceptionStaleHandle if an attempt + * pointer. The checked handle will throw a Stale if an attempt * is made to access the object referenced by the handle and that object has * been destroyed. **/ template -class LLCheckedHandle: private boost::noncopyable +class LLCheckedHandle: public LLCheckedHandleBase { public: + LLCheckedHandle(LLHandle handle): mHandle(handle) { } /** - * Retrieve the underlying pointer by resolving the handle. If the handle - * returns NULL for the pointer throw an LLExeceptionStaleHandle exception. + * Test the underlying handle. If it is no longer valid, throw a Stale exception. */ - T* get() const + void check() const { T* ptr = mHandle.get(); if (!ptr) - BOOST_THROW_EXCEPTION(LLExeceptionStaleHandle()); - return ptr; + BOOST_THROW_EXCEPTION(Stale()); } /** - * Test the handle to see if it is still valid. Returns true if it is, - * false if it is not. Does not trow. + * Cast back to an appropriate handle */ - bool test() const - { - return (mHandle.get() != NULL); - } - - /** - * Test the underlying handle. If it is no longer valid, throw a LLExeceptionStaleHandle. - */ - void check() const - { - get(); - } - - /** - * Get the contained handle. - */ - LLHandle getHandle() const + operator LLHandle() const { return mHandle; } @@ -281,22 +271,26 @@ public: * Converts the LLCheckedHandle to a bool. Allows for if (chkdHandle) {} * Does not throw. */ - operator bool() const + explicit operator bool() const { - return test(); + return (mHandle.get() != NULL); } /** * Attempt to call a method or access a member in the structure referenced * by the handle. If the handle no longer points to a valid structure - * throw a LLExeceptionStaleHandle. + * throw a Stale. */ T* operator ->() const { - return get(); + T* ptr = mHandle.get(); + if (!ptr) + BOOST_THROW_EXCEPTION(Stale()); + return ptr; } private: + LLHandle mHandle; }; -- cgit v1.2.3 From 694fe9cfc5a63f825bf77f4630c2da57c16171bf Mon Sep 17 00:00:00 2001 From: Rider Linden Date: Thu, 21 Jul 2016 13:03:40 -0700 Subject: explicit not available for conversion operators in Linux yet --- indra/llcommon/llhandle.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llhandle.h b/indra/llcommon/llhandle.h index 8e073aab27..feb5f41848 100644 --- a/indra/llcommon/llhandle.h +++ b/indra/llcommon/llhandle.h @@ -271,7 +271,7 @@ public: * Converts the LLCheckedHandle to a bool. Allows for if (chkdHandle) {} * Does not throw. */ - explicit operator bool() const + /*explicit*/ operator bool() const // explicit conversion operator not available with Linux compiler { return (mHandle.get() != NULL); } -- cgit v1.2.3 From 1ed76c382e8b87bff02b6d37cf8acd7f6b1f8063 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 17 Aug 2016 10:45:06 -0400 Subject: MAINT-5011: Add llexception_test.cpp with tests (and conclusions). llexception_test.cpp is an unusual test source in that it need not be verified on every build, so its invocation in indra/llcommon/CMakeLists.txt is commented out with that remark. Its purpose is to help a developer decide what base class(es) to use for LLException, how to throw and how to catch. Our current conclusions are written up as comments in llexception_test.cpp. Added CRASH_ON_UNHANDLED_EXCEPTION() and LOG_UNHANDLED_EXCEPTION() macros to llexception.h -- macros to log __FILE__, __LINE__ and __PRETTY_FUNCTION__ of the catch site. These invoke functions in llexception.cpp so we don't need to #include llerror.h for every possible catch site. --- indra/llcommon/CMakeLists.txt | 8 +- indra/llcommon/llexception.cpp | 41 ++++ indra/llcommon/llexception.h | 27 ++- indra/llcommon/tests/llexception_test.cpp | 308 ++++++++++++++++++++++++++++++ 4 files changed, 377 insertions(+), 7 deletions(-) create mode 100644 indra/llcommon/llexception.cpp create mode 100644 indra/llcommon/tests/llexception_test.cpp (limited to 'indra/llcommon') diff --git a/indra/llcommon/CMakeLists.txt b/indra/llcommon/CMakeLists.txt index 44f45144e5..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 @@ -316,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}") @@ -329,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/llexception.cpp b/indra/llcommon/llexception.cpp new file mode 100644 index 0000000000..f48509b2aa --- /dev/null +++ b/indra/llcommon/llexception.cpp @@ -0,0 +1,41 @@ +/** + * @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 +// external library headers +#include +// other Linden headers +#include "llerror.h" + +void crash_on_unhandled_exception_(const char* file, int line, const char* pretty_function) +{ + // LL_ERRS() terminates, but also propagates message into crash dump. + LL_ERRS() << file << "(" << line << "): Unhandled exception caught in " << pretty_function + << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL; +} + +void log_unhandled_exception_(const char* file, int line, const char* pretty_function, + const LLContinueError& e) +{ + // Use LL_WARNS() because we seriously do not expect this to happen + // routinely, but we DO expect to return from this function. Deriving your + // exception from LLContinueError implies that such an exception should + // NOT be fatal to the viewer, only to its current task. + LL_WARNS() << file << "(" << line << "): Unhandled " << typeid(e).name() + << " exception caught in " << pretty_function + << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL; +} diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 3ac2f4762f..68bd20fbcd 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -15,15 +15,20 @@ #include #include +// "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. It is itself a subclass of - * boost::exception; use catch (const boost::exception& e) clause to log the - * string from boost::diagnostic_information(e). + * 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. * - * Since it is also derived from std::exception, a generic catch (const - * std::exception&) should also work, though what() is unlikely to be as - * informative as boost::diagnostic_information(). + * 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(). * * Please use BOOST_THROW_EXCEPTION() * http://www.boost.org/doc/libs/release/libs/exception/doc/BOOST_THROW_EXCEPTION.html @@ -60,4 +65,14 @@ struct LLContinueError: public LLException {} }; +/// Call this macro from a catch (...) clause +#define CRASH_ON_UNHANDLED_EXCEPTION() \ + crash_on_unhandled_exception_(__FILE__, __LINE__, __PRETTY_FUNCTION__) +void crash_on_unhandled_exception_(const char*, int, const char*); + +/// Call this from a catch (const LLContinueError&) clause +#define LOG_UNHANDLED_EXCEPTION(EXC) \ + log_unhandled_exception_(__FILE__, __LINE__, __PRETTY_FUNCTION__, EXC) +void log_unhandled_exception_(const char*, int, const char*, const LLContinueError&); + #endif /* ! defined(LL_LLEXCEPTION_H) */ 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 +// external library headers +#include +// 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 +void plain_throw(const std::string& what) +{ + throw EXC(what); +} + +template +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 +void catch_both_several(const std::string& what) +{ + std::cout << std::string(20, '*') << "plain_throw<" << what << ">" << std::endl; + catch_several(plain_throw, "plain_throw<" + what + ">"); + + std::cout << std::string(20, '*') << "boost_throw<" << what << ">" << std::endl; + catch_several(boost_throw, "boost_throw<" + what + ">"); +} + +/***************************************************************************** +* TUT +*****************************************************************************/ +namespace tut +{ + struct llexception_data + { + }; + typedef test_group 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"); + + std::cout << center("FromBoth", '=', margin) << std::endl; + catch_both_several("FromBoth"); + + std::cout << center("FromBoost", '=', margin) << std::endl; + // can't throw with BOOST_THROW_EXCEPTION(), just use catch_several() + catch_several(plain_throw, "plain_throw"); + + std::cout << center("FromNeither", '=', margin) << std::endl; + // can't throw this with BOOST_THROW_EXCEPTION() either + catch_several(plain_throw, "plain_throw"); + + 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 -- cgit v1.2.3 From 5e9d2f57c82a57307a48afea09aa539b9fa80abf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 17 Aug 2016 11:36:24 -0400 Subject: MAINT-5011: Use LLTHROW() instead of plain BOOST_THROW_EXCEPTION(). A level of preprocessor indirection lets us later change the implementation if desired. --- indra/llcommon/lldependencies.cpp | 4 ++-- indra/llcommon/lleventcoro.cpp | 4 ++-- indra/llcommon/llevents.cpp | 18 ++++++++---------- indra/llcommon/llexception.h | 16 ++++++++++------ indra/llcommon/llleap.cpp | 6 +++--- indra/llcommon/llprocess.cpp | 20 +++++++++----------- indra/llcommon/llthreadsafequeue.cpp | 14 +++++++------- indra/llcommon/lluuid.cpp | 2 +- indra/llcommon/tests/wrapllerrs.h | 3 +-- 9 files changed, 43 insertions(+), 44 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/lldependencies.cpp b/indra/llcommon/lldependencies.cpp index 87a699ff14..0d5757effd 100644 --- a/indra/llcommon/lldependencies.cpp +++ b/indra/llcommon/lldependencies.cpp @@ -39,8 +39,8 @@ #include #include #include -#include // other Linden headers +#include "llexception.h" LLDependenciesBase::VertexList LLDependenciesBase::topo_sort(int vertices, const EdgeList& edges) const { @@ -77,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); - BOOST_THROW_EXCEPTION(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/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index f444530a17..56367b8f54 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -34,12 +34,12 @@ #include // std headers // external library headers -#include // other Linden headers #include "llsdserialize.h" #include "llerror.h" #include "llcoros.h" #include "llmake.h" +#include "llexception.h" #include "lleventfilter.h" @@ -352,7 +352,7 @@ LLSD errorException(const LLEventWithID& result, const std::string& desc) // returning it, deliver it via exception. if (result.second) { - BOOST_THROW_EXCEPTION(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/llevents.cpp b/indra/llcommon/llevents.cpp index 50919edb8e..19d700a3b0 100644 --- a/indra/llcommon/llevents.cpp +++ b/indra/llcommon/llevents.cpp @@ -45,7 +45,6 @@ #include // external library headers #include -#include #if LL_WINDOWS #pragma warning (push) #pragma warning (disable : 4701) // compiler thinks might use uninitialized var, but no @@ -58,6 +57,7 @@ #include "stringize.h" #include "llerror.h" #include "llsdutil.h" +#include "llexception.h" #if LL_MSVC #pragma warning (disable : 4702) #endif @@ -175,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) { - BOOST_THROW_EXCEPTION(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 @@ -327,9 +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()) { - BOOST_THROW_EXCEPTION( - 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 @@ -355,9 +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); - BOOST_THROW_EXCEPTION( - 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; @@ -421,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. - BOOST_THROW_EXCEPTION(OrderChange(out.str())); + LLTHROW(OrderChange(out.str())); } // This node becomes the previous one. previous = dmi->second; @@ -611,7 +609,7 @@ bool LLListenerOrPumpName::operator()(const LLSD& event) const { if (! mListener) { - BOOST_THROW_EXCEPTION(Empty("attempting to call uninitialized")); + LLTHROW(Empty("attempting to call uninitialized")); } return (*mListener)(event); } diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 68bd20fbcd..e9e25ae689 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -14,6 +14,7 @@ #include #include +#include // "Found someone who can comfort me // But there are always exceptions..." @@ -30,12 +31,6 @@ * all we need to do with an exception is log it, in most places we should * catch (...) and log boost::current_exception_diagnostic_information(). * - * Please use BOOST_THROW_EXCEPTION() - * http://www.boost.org/doc/libs/release/libs/exception/doc/BOOST_THROW_EXCEPTION.html - * to throw viewer exceptions whenever possible. This enriches the exception's - * diagnostic_information() with the source file, line and containing function - * of the BOOST_THROW_EXCEPTION() macro. - * * 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 @@ -65,6 +60,15 @@ struct LLContinueError: public LLException {} }; +/** + * 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() \ crash_on_unhandled_exception_(__FILE__, __LINE__, __PRETTY_FUNCTION__) diff --git a/indra/llcommon/llleap.cpp b/indra/llcommon/llleap.cpp index a8bb9bc53a..c87d2a3e58 100644 --- a/indra/llcommon/llleap.cpp +++ b/indra/llcommon/llleap.cpp @@ -21,7 +21,6 @@ #include #include #include -#include // other Linden headers #include "llerror.h" #include "llstring.h" @@ -34,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 @@ -70,7 +70,7 @@ public: // Rule out empty vector if (plugin.empty()) { - BOOST_THROW_EXCEPTION(Error("no plugin command")); + LLTHROW(Error("no plugin command")); } // Don't leave desc empty either, but in this case, if we weren't @@ -113,7 +113,7 @@ public: // If that didn't work, no point in keeping this LLLeap object. if (! mChild) { - BOOST_THROW_EXCEPTION(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/llprocess.cpp b/indra/llcommon/llprocess.cpp index ca19c94736..8c321d06b9 100644 --- a/indra/llcommon/llprocess.cpp +++ b/indra/llcommon/llprocess.cpp @@ -34,12 +34,12 @@ #include "llapr.h" #include "apr_signal.h" #include "llevents.h" +#include "llexception.h" #include #include #include #include -#include #include #include #include @@ -531,9 +531,8 @@ LLProcess::LLProcess(const LLSDOrParams& params): if (! params.validateBlock(true)) { - BOOST_THROW_EXCEPTION( - LLProcessError(STRINGIZE("not launched: failed parameter validation\n" - << LLSDNotationStreamer(params)))); + LLTHROW(LLProcessError(STRINGIZE("not launched: failed parameter validation\n" + << LLSDNotationStreamer(params)))); } mPostend = params.postend; @@ -598,11 +597,10 @@ LLProcess::LLProcess(const LLSDOrParams& params): } else { - BOOST_THROW_EXCEPTION( - 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. @@ -681,7 +679,7 @@ LLProcess::LLProcess(const LLSDOrParams& params): if (ll_apr_warn_status(apr_proc_create(&mProcess, argv[0], &argv[0], NULL, procattr, gAPRPoolp))) { - BOOST_THROW_EXCEPTION(LLProcessError(STRINGIZE(params << " failed"))); + LLTHROW(LLProcessError(STRINGIZE(params << " failed"))); } // arrange to call status_callback() @@ -1066,7 +1064,7 @@ PIPETYPE& LLProcess::getPipe(FILESLOT slot) PIPETYPE* wp = getPipePtr(error, slot); if (! wp) { - BOOST_THROW_EXCEPTION(NoPipe(error)); + LLTHROW(NoPipe(error)); } return *wp; } diff --git a/indra/llcommon/llthreadsafequeue.cpp b/indra/llcommon/llthreadsafequeue.cpp index a004618e96..491f920c0f 100644 --- a/indra/llcommon/llthreadsafequeue.cpp +++ b/indra/llcommon/llthreadsafequeue.cpp @@ -26,8 +26,8 @@ #include "linden_common.h" #include #include -#include #include "llthreadsafequeue.h" +#include "llexception.h" @@ -42,13 +42,13 @@ LLThreadSafeQueueImplementation::LLThreadSafeQueueImplementation(apr_pool_t * po { if(mOwnsPool) { apr_status_t status = apr_pool_create(&mPool, 0); - if(status != APR_SUCCESS) BOOST_THROW_EXCEPTION(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) BOOST_THROW_EXCEPTION(LLThreadSafeQueueError("failed to allocate queue")); + if(status != APR_SUCCESS) LLTHROW(LLThreadSafeQueueError("failed to allocate queue")); } @@ -69,9 +69,9 @@ void LLThreadSafeQueueImplementation::pushFront(void * element) apr_status_t status = apr_queue_push(mQueue, element); if(status == APR_EINTR) { - BOOST_THROW_EXCEPTION(LLThreadSafeQueueInterrupt()); + LLTHROW(LLThreadSafeQueueInterrupt()); } else if(status != APR_SUCCESS) { - BOOST_THROW_EXCEPTION(LLThreadSafeQueueError("push failed")); + LLTHROW(LLThreadSafeQueueError("push failed")); } else { ; // Success. } @@ -89,9 +89,9 @@ void * LLThreadSafeQueueImplementation::popBack(void) apr_status_t status = apr_queue_pop(mQueue, &element); if(status == APR_EINTR) { - BOOST_THROW_EXCEPTION(LLThreadSafeQueueInterrupt()); + LLTHROW(LLThreadSafeQueueInterrupt()); } else if(status != APR_SUCCESS) { - BOOST_THROW_EXCEPTION(LLThreadSafeQueueError("pop failed")); + LLTHROW(LLThreadSafeQueueError("pop failed")); } else { return element; } diff --git a/indra/llcommon/lluuid.cpp b/indra/llcommon/lluuid.cpp index 785cf47926..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 ) BOOST_THROW_EXCEPTION(bad_input_data()); +if( s == 0 ) LLTHROW(bad_input_data()); ret = ret * 85 + (s-encodeTable); } return ret; diff --git a/indra/llcommon/tests/wrapllerrs.h b/indra/llcommon/tests/wrapllerrs.h index fa16fd6915..9a4bbbd630 100644 --- a/indra/llcommon/tests/wrapllerrs.h +++ b/indra/llcommon/tests/wrapllerrs.h @@ -40,7 +40,6 @@ #include #include #include -#include #include #include @@ -79,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. - BOOST_THROW_EXCEPTION(FatalException(message)); + LLTHROW(FatalException(message)); } std::string error; -- cgit v1.2.3 From 993f54f6e91d78a9c2e1389ad878d6bd46e9be5b Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 17 Aug 2016 15:40:03 -0400 Subject: MAINT-5011: Try to enrich catch (...) logging throughout viewer. Turns out we have a surprising number of catch (...) clauses in the viewer code base. If all we currently do is LL_ERRS() << "unknown exception" << LL_ENDL; then call CRASH_ON_UNHANDLED_EXCEPTION() instead. If what we do is LL_WARNS() << "unknown exception" << LL_ENDL; then call LOG_UNHANDLED_EXCEPTION() instead. Since many places need LOG_UNHANDLED_EXCEPTION() and nobody catches LLContinueError yet, eliminate LLContinueError& parameter from LOG_UNHANDLED_EXCEPTION(). This permits us to use the same log message as CRASH_ON_UNHANDLED_EXCEPTION(), just with a different severity level. Where a catch (...) clause actually provides contextual information, or makes an error string, add boost::current_exception_diagnostic_information() to try to figure out actual exception class and message. --- indra/llcommon/llexception.cpp | 29 ++++++++++++++++++----------- indra/llcommon/llexception.h | 9 +++++---- 2 files changed, 23 insertions(+), 15 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index f48509b2aa..9a6dfee3f1 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -21,21 +21,28 @@ // other Linden headers #include "llerror.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) +{ + // log same message but allow caller-specified severity level + // lllog() is the macro underlying LL_ERRS(), LL_WARNS() et al. + lllog(level, false) << file << "(" << line << "): Unhandled exception caught in " + << pretty_function + << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL; +} +} + void crash_on_unhandled_exception_(const char* file, int line, const char* pretty_function) { - // LL_ERRS() terminates, but also propagates message into crash dump. - LL_ERRS() << file << "(" << line << "): Unhandled exception caught in " << pretty_function - << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL; + // LL_ERRS() terminates and propagates message into crash dump. + log_unhandled_exception_(LLError::LEVEL_ERROR, file, line, pretty_function); } -void log_unhandled_exception_(const char* file, int line, const char* pretty_function, - const LLContinueError& e) +void log_unhandled_exception_(const char* file, int line, const char* pretty_function) { // Use LL_WARNS() because we seriously do not expect this to happen - // routinely, but we DO expect to return from this function. Deriving your - // exception from LLContinueError implies that such an exception should - // NOT be fatal to the viewer, only to its current task. - LL_WARNS() << file << "(" << line << "): Unhandled " << typeid(e).name() - << " exception caught in " << pretty_function - << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL; + // routinely, but we DO expect to return from this function. + log_unhandled_exception_(LLError::LEVEL_WARN, file, line, pretty_function); } diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index e9e25ae689..2a0f5e79eb 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -74,9 +74,10 @@ struct LLContinueError: public LLException crash_on_unhandled_exception_(__FILE__, __LINE__, __PRETTY_FUNCTION__) void crash_on_unhandled_exception_(const char*, int, const char*); -/// Call this from a catch (const LLContinueError&) clause -#define LOG_UNHANDLED_EXCEPTION(EXC) \ - log_unhandled_exception_(__FILE__, __LINE__, __PRETTY_FUNCTION__, EXC) -void log_unhandled_exception_(const char*, int, const char*, const LLContinueError&); +/// 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() \ + log_unhandled_exception_(__FILE__, __LINE__, __PRETTY_FUNCTION__) +void log_unhandled_exception_(const char*, int, const char*); #endif /* ! defined(LL_LLEXCEPTION_H) */ -- cgit v1.2.3 From c7bf8af6378a54c3d03e77b161b7ba3b36186576 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 18 Aug 2016 11:06:51 -0400 Subject: MAINT-5011: Use BOOST_CURRENT_FUNCTION instead of __PRETTY_FUNCTION__ since Visual Studio doesn't know __PRETTY_FUNCTION__, and Boost already has a portable macro to Do The Right Thing. --- indra/llcommon/llexception.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 2a0f5e79eb..384b2271fb 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -15,6 +15,7 @@ #include #include #include +#include // "Found someone who can comfort me // But there are always exceptions..." @@ -71,13 +72,13 @@ struct LLContinueError: public LLException /// Call this macro from a catch (...) clause #define CRASH_ON_UNHANDLED_EXCEPTION() \ - crash_on_unhandled_exception_(__FILE__, __LINE__, __PRETTY_FUNCTION__) + crash_on_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION) void crash_on_unhandled_exception_(const char*, int, const char*); /// 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() \ - log_unhandled_exception_(__FILE__, __LINE__, __PRETTY_FUNCTION__) + log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION) void log_unhandled_exception_(const char*, int, const char*); #endif /* ! defined(LL_LLEXCEPTION_H) */ -- cgit v1.2.3 From 4d10172d8b2c72fa809e322a3b4ff326b19ff340 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 18 Aug 2016 17:33:44 -0400 Subject: MAINT-5011: Catch unhandled exceptions in LLCoros coroutines. Wrap coroutine call in try/catch in top-level coroutine wrapper function LLCoros::toplevel(). Distinguish exception classes derived from LLContinueError (log and continue) from all others (crash with LL_ERRS). Enhance CRASH_ON_UNHANDLED_EXCEPTIONS() and LOG_UNHANDLED_EXCEPTIONS() macros to accept a context string to supplement the log message. This lets us replace many places that called boost::current_exception_diagnostic_information() with LOG_UNHANDLED_EXCEPTIONS() instead, since the explicit calls were mostly to log supplemental information. Provide supplemental information (coroutine name, function parameters) for some of the previous LOG_UNHANDLED_EXCEPTIONS() calls. This information duplicates LL_DEBUGS() information at the top of these functions, but in a typical log file we wouldn't see the LL_DEBUGS() message. Eliminate a few catch (std::exception e) clauses: the information we get from boost::current_exception_diagnostic_information() in a catch (...) clause makes it unnecessary to distinguish. In a few cases, add a final 'throw;' to a catch (...) clause: having logged the local context info, propagate the exception to be caught by higher-level try/catch. In a couple places, couldn't resist reconciling indentation within a particular function: tabs where the rest of the function uses tabs, spaces where the rest of the function uses spaces. In LLLogin::Impl::loginCoro(), eliminate some confusing comments about an array of rewritten URIs that date back to a long-deleted implementation. --- indra/llcommon/llcoros.cpp | 14 +++++++++++++- indra/llcommon/llexception.cpp | 21 ++++++++++++++------- indra/llcommon/llexception.h | 13 +++++++------ 3 files changed, 34 insertions(+), 14 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index d16bf0160b..4ee8e6d796 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,18 @@ 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& e) + { + LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); + } + catch (...) + { + 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/llexception.cpp b/indra/llcommon/llexception.cpp index 9a6dfee3f1..791b115a8c 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -24,25 +24,32 @@ 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 char* file, int line, const char* pretty_function, + const std::string& context) { // log same message but allow caller-specified severity level // lllog() is the macro underlying LL_ERRS(), LL_WARNS() et al. lllog(level, false) << file << "(" << line << "): Unhandled exception caught in " - << pretty_function - << ":\n" << boost::current_exception_diagnostic_information() << LL_ENDL; + << 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) +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); + log_unhandled_exception_(LLError::LEVEL_ERROR, file, line, pretty_function, context); } -void log_unhandled_exception_(const char* file, int line, const char* pretty_function) +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); + log_unhandled_exception_(LLError::LEVEL_WARN, file, line, pretty_function, context); } diff --git a/indra/llcommon/llexception.h b/indra/llcommon/llexception.h index 384b2271fb..dfcb7c192f 100644 --- a/indra/llcommon/llexception.h +++ b/indra/llcommon/llexception.h @@ -31,6 +31,7 @@ * 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 @@ -71,14 +72,14 @@ struct LLContinueError: public LLException #define LLTHROW(x) BOOST_THROW_EXCEPTION(x) /// Call this macro from a catch (...) clause -#define CRASH_ON_UNHANDLED_EXCEPTION() \ - crash_on_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION) -void crash_on_unhandled_exception_(const char*, int, const char*); +#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() \ - log_unhandled_exception_(__FILE__, __LINE__, BOOST_CURRENT_FUNCTION) -void log_unhandled_exception_(const char*, int, const char*); +#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) */ -- cgit v1.2.3 From 17382b22e0fff078703a667cfd199db7d079ddbd Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 25 Aug 2016 14:04:36 -0400 Subject: MAINT-5011: Remove unreferenced param name to avoid fatal warning --- indra/llcommon/llcoros.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 4ee8e6d796..7f4c1780b8 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -240,7 +240,7 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla { callable(); } - catch (const LLContinueError& e) + catch (const LLContinueError&) { LOG_UNHANDLED_EXCEPTION(STRINGIZE("coroutine " << data->mName)); } -- cgit v1.2.3 From abfe05c1b3d24ed820cf084ece3f3610ec35fa21 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 26 Aug 2016 14:20:11 -0400 Subject: MAINT-5011: Add comments to LLCoros::toplevel() exception handlers. --- indra/llcommon/llcoros.cpp | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 7f4c1780b8..4db63937aa 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -242,10 +242,15 @@ void LLCoros::toplevel(coro::self& self, CoroData* data, const callable_t& calla } 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 -- cgit v1.2.3 From c2c3086d6c153883a43664446ac3f05a11a61a96 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 26 Aug 2016 15:50:37 -0400 Subject: MAINT-5011: Catch LLContinueError in LLStopWhenHandled::operator(). This means that an exception derived from LLContinueError thrown in an LLEventPump listener won't prevent other listeners on the same LLEventPump from receiving that event. --- indra/llcommon/llevents.h | 32 ++++++++++++++++++++++++++------ 1 file changed, 26 insertions(+), 6 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llevents.h b/indra/llcommon/llevents.h index 8ff337911d..1526128725 100644 --- a/indra/llcommon/llevents.h +++ b/indra/llcommon/llevents.h @@ -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; } }; -- cgit v1.2.3 From 0c2442eb684a060d5582c154c95cb995a64ae70c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Mon, 29 Aug 2016 18:48:47 +0000 Subject: MAINT-5011: Fix abbreviateFile() test to run under .../indra/ path. This particular test relied on there being exactly one instance of the string "indra" in the source file's __FILE__ path -- which is usually true, but not if the developer clones the viewer source repo under a parent directory whose path itself contains "indra". Fix to handle any number of occurrences. --- indra/llcommon/tests/llerror_test.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') 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, -- cgit v1.2.3 From 8118762c124d2de8db40836a28b8bf12ecbefbf7 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 31 Aug 2016 17:16:43 -0400 Subject: MAINT-5011: Add log tag to LOG_UNHANDLED_EXCEPTION() log messages. --- indra/llcommon/llexception.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index 791b115a8c..1c39c0f827 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -29,8 +29,8 @@ void log_unhandled_exception_(LLError::ELevel level, { // log same message but allow caller-specified severity level // lllog() is the macro underlying LL_ERRS(), LL_WARNS() et al. - lllog(level, false) << file << "(" << line << "): Unhandled exception caught in " - << pretty_function; + lllog(level, false, "LLException") + << file << "(" << line << "): Unhandled exception caught in " << pretty_function; if (! context.empty()) { LL_CONT << ": " << context; -- cgit v1.2.3 From 31d3d654f156351e3cf29f97fd97cbda046ae650 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 1 Sep 2016 15:21:54 -0400 Subject: MAINT-5011: Advise against lllog() instead of (e.g.) LL_INFOS(). --- indra/llcommon/llerror.h | 37 +++++++++++++++++++++++-------------- 1 file changed, 23 insertions(+), 14 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 3beef65723..8e13ec6431 100644 --- a/indra/llcommon/llerror.h +++ b/indra/llcommon/llerror.h @@ -305,22 +305,31 @@ 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. 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__}; \ + ::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(); \ (*_out) //Use this construct if you need to do computation in the middle of a -- cgit v1.2.3 From 9be4e7e448769cb82dc7d82e93bee4f1cd2b3e6c Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 1 Sep 2016 19:32:31 -0400 Subject: MAINT-5232: Introduce LL_VLOGS() macro: log call with variable level. In some places we want to log the same information but with different severity depending on specifics. In other cases we need to test the availability of the logging subsystem before engaging it. LL_VLOGS() accepts an LLError::ELevel argument that can differ with each call, while retaining the desirable feature of deciding only once for each level. --- indra/llcommon/llerror.h | 72 ++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 60 insertions(+), 12 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llerror.h b/indra/llcommon/llerror.h index 8e13ec6431..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. @@ -314,24 +315,29 @@ typedef LLError::NoClassInfo _LL_CLASS_TO_LOG; // 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. lllog() assumes its ELevel argument never varies. +// 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__}; \ - ::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(); \ +#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: // @@ -372,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 -- cgit v1.2.3 From 1ed351e28f1134ba06fd9459927d0d146e204fbf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 1 Sep 2016 19:46:39 -0400 Subject: MAINT-5011: Use LL_VLOGS() rather than raw lllog() macro. Raw lllog() doesn't work for varying log level, which is why LL_VLOGS() exists. --- indra/llcommon/llexception.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index 1c39c0f827..25aa5847a2 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -28,8 +28,7 @@ void log_unhandled_exception_(LLError::ELevel level, const std::string& context) { // log same message but allow caller-specified severity level - // lllog() is the macro underlying LL_ERRS(), LL_WARNS() et al. - lllog(level, false, "LLException") + LL_VLOGS(level, "LLException") << file << "(" << line << "): Unhandled exception caught in " << pretty_function; if (! context.empty()) { -- cgit v1.2.3 From 1804da89eea38615a4dd9532757b7ef7c35d2be6 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 2 Sep 2016 14:00:18 -0400 Subject: MAINT-5011: Abbreviate __FILE__ path in log_unhandled_exception_(). LLError::abbreviateFile() is specifically to avoid cluttering log output with the prefix of an absolute file path on the original build system, pointless for anyone trying to read the log. --- indra/llcommon/llexception.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llexception.cpp b/indra/llcommon/llexception.cpp index 25aa5847a2..b32ec2c9c9 100644 --- a/indra/llcommon/llexception.cpp +++ b/indra/llcommon/llexception.cpp @@ -20,6 +20,7 @@ #include // other Linden headers #include "llerror.h" +#include "llerrorcontrol.h" namespace { // used by crash_on_unhandled_exception_() and log_unhandled_exception_() @@ -28,8 +29,8 @@ void log_unhandled_exception_(LLError::ELevel level, const std::string& context) { // log same message but allow caller-specified severity level - LL_VLOGS(level, "LLException") - << file << "(" << line << "): Unhandled exception caught in " << pretty_function; + LL_VLOGS(level, "LLException") << LLError::abbreviateFile(file) + << "(" << line << "): Unhandled exception caught in " << pretty_function; if (! context.empty()) { LL_CONT << ": " << context; -- cgit v1.2.3 From a5adabb4f6660144345ddbd0b647130fd2ca6461 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Tue, 6 Sep 2016 10:04:19 -0400 Subject: add protections against failed memory allocations in VBO and aligned memory --- indra/llcommon/llmemory.h | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llmemory.h b/indra/llcommon/llmemory.h index 0fb257aab1..575edddc43 100644 --- a/indra/llcommon/llmemory.h +++ b/indra/llcommon/llmemory.h @@ -110,11 +110,15 @@ template T* LL_NEXT_ALIGNED_ADDRESS_64(T* address) #if defined(LL_WINDOWS) return _aligned_malloc(size, align); #else + char* aligned = NULL; void* mem = malloc( size + (align - 1) + sizeof(void*) ); - char* aligned = ((char*)mem) + sizeof(void*); - aligned += align - ((uintptr_t)aligned & (align - 1)); + if (mem) + { + aligned = ((char*)mem) + sizeof(void*); + aligned += align - ((uintptr_t)aligned & (align - 1)); - ((void**)aligned)[-1] = mem; + ((void**)aligned)[-1] = mem; + } return aligned; #endif } -- cgit v1.2.3 From 72f4c72001789ac8de425c6d0a5f59b0ffda3726 Mon Sep 17 00:00:00 2001 From: Oz Linden Date: Tue, 6 Sep 2016 10:21:28 -0400 Subject: downgrade spammy LLCoros logging to DEBUG --- indra/llcommon/llcoros.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index d16bf0160b..5bbce4325b 100644 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -131,9 +131,9 @@ bool LLCoros::cleanup(const LLSD&) if ((previousCount < 5) || !(previousCount % 50)) { if (previousCount < 5) - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL; else - LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << "("<< previousCount << ")" << LL_ENDL; } // The erase() call will invalidate its passed iterator value -- @@ -185,9 +185,9 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const if ((previousCount < 5) || !(previousCount % 50)) { if (previousCount < 5) - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL; else - LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL; + LL_DEBUGS("LLCoros") << "LLCoros: launching coroutine " << name << "(" << previousCount << ")" << LL_ENDL; } @@ -223,7 +223,7 @@ std::string LLCoros::getName() const void LLCoros::setStackSize(S32 stacksize) { - LL_INFOS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL; + LL_DEBUGS("LLCoros") << "Setting coroutine stack size to " << stacksize << LL_ENDL; mStackSize = stacksize; } -- cgit v1.2.3 From 67c401047a472d579cffe629519090ea8ef5c570 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 6 Sep 2016 20:48:16 -0400 Subject: MAINT-5011: Ensure BlockTimer::mStartTime is unconditionally set. Previous logic could possibly leave mStartTime uninitialized, producing fatal warnings with gcc 4.7. --- indra/llcommon/llfasttimer.h | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'indra/llcommon') diff --git a/indra/llcommon/llfasttimer.h b/indra/llcommon/llfasttimer.h index 2370253078..f56e5596f5 100644 --- a/indra/llcommon/llfasttimer.h +++ b/indra/llcommon/llfasttimer.h @@ -296,7 +296,16 @@ LL_FORCE_INLINE BlockTimer::BlockTimer(BlockTimerStatHandle& timer) { #if LL_FAST_TIMER_ON BlockTimerStackRecord* cur_timer_data = LLThreadLocalSingletonPointer::getInstance(); - if (!cur_timer_data) return; + if (!cur_timer_data) + { + // How likely is it that + // LLThreadLocalSingletonPointer::getInstance() will return NULL? + // Even without researching, what we can say is that if we exit + // without setting mStartTime at all, gcc 4.7 produces (fatal) + // warnings about a possibly-uninitialized data member. + mStartTime = 0; + return; + } TimeBlockAccumulator& accumulator = timer.getCurrentAccumulator(); accumulator.mActiveCount++; // keep current parent as long as it is active when we are -- cgit v1.2.3