diff options
Diffstat (limited to 'indra/llcommon')
-rw-r--r-- | indra/llcommon/llapp.cpp | 36 | ||||
-rw-r--r-- | indra/llcommon/lleventcoro.cpp | 80 |
2 files changed, 101 insertions, 15 deletions
diff --git a/indra/llcommon/llapp.cpp b/indra/llcommon/llapp.cpp index 421af3006e..3dab632aef 100644 --- a/indra/llcommon/llapp.cpp +++ b/indra/llcommon/llapp.cpp @@ -49,6 +49,8 @@ #include "google_breakpad/exception_handler.h" #include "stringize.h" #include "llcleanup.h" +#include "llevents.h" +#include "llsdutil.h" // // Signal handling @@ -561,10 +563,42 @@ void LLApp::runErrorHandler() LLApp::setStopped(); } +namespace +{ + +static std::map<LLApp::EAppStatus, const char*> statusDesc +{ + { LLApp::APP_STATUS_RUNNING, "running" }, + { LLApp::APP_STATUS_QUITTING, "quitting" }, + { LLApp::APP_STATUS_STOPPED, "stopped" }, + { LLApp::APP_STATUS_ERROR, "error" } +}; + +} // anonymous namespace + // static void LLApp::setStatus(EAppStatus status) { - sStatus = status; + sStatus = status; + + // This can also happen very late in the application lifecycle -- don't + // resurrect a deleted LLSingleton + if (! LLEventPumps::wasDeleted()) + { + // notify interested parties of status change + LLSD statsd; + auto found = statusDesc.find(status); + if (found != statusDesc.end()) + { + statsd = found->second; + } + else + { + // unknown status? at least report value + statsd = LLSD::Integer(status); + } + LLEventPumps::instance().obtain("LLApp").post(llsd::map("status", statsd)); + } } diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index b374c9fa04..23e0012a1a 100644 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -32,6 +32,7 @@ #include "lleventcoro.h" // STL headers #include <chrono> +#include <exception> // std headers // external library headers #include <boost/fiber/operations.hpp> @@ -40,6 +41,7 @@ #include "llsdutil.h" #include "llerror.h" #include "llcoros.h" +#include "stringize.h" namespace { @@ -106,29 +108,39 @@ void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value) void llcoro::suspend() { + LLCoros::checkStop(); + LLCoros::TempStatus st("waiting one tick"); boost::this_fiber::yield(); } void llcoro::suspendUntilTimeout(float seconds) { + LLCoros::checkStop(); // The fact that we accept non-integer seconds means we should probably // use granularity finer than one second. However, given the overhead of // the rest of our processing, it seems silly to use granularity finer // than a millisecond. + LLCoros::TempStatus st(STRINGIZE("waiting for " << seconds << "s")); boost::this_fiber::sleep_for(std::chrono::milliseconds(long(seconds * 1000))); } namespace { -LLBoundListener postAndSuspendSetup(const std::string& callerName, - const std::string& listenerName, - LLCoros::Promise<LLSD>& promise, - const LLSD& event, - const LLEventPumpOrPumpName& requestPumpP, - const LLEventPumpOrPumpName& replyPumpP, - const LLSD& replyPumpNamePath) +// returns a listener on replyPumpP, also on "mainloop" -- both should be +// stored in LLTempBoundListeners on the caller's stack frame +std::pair<LLBoundListener, LLBoundListener> +postAndSuspendSetup(const std::string& callerName, + const std::string& listenerName, + LLCoros::Promise<LLSD>& promise, + const LLSD& event, + const LLEventPumpOrPumpName& requestPumpP, + const LLEventPumpOrPumpName& replyPumpP, + const LLSD& replyPumpNamePath) { + // Before we get any farther -- should we be stopping instead of + // suspending? + LLCoros::checkStop(); // Get the consuming attribute for THIS coroutine, the one that's about to // suspend. Don't call get_consuming() in the lambda body: that would // return the consuming attribute for some other coroutine, most likely @@ -138,6 +150,38 @@ LLBoundListener postAndSuspendSetup(const std::string& callerName, // value to the promise, thus fulfilling its future llassert_always_msg(replyPumpP, ("replyPump required for " + callerName)); LLEventPump& replyPump(replyPumpP.getPump()); + // The relative order of the two listen() calls below would only matter if + // "LLApp" were an LLEventMailDrop. But if we ever go there, we'd want to + // notice the pending LLApp status first. + LLBoundListener stopper( + LLEventPumps::instance().obtain("LLApp").listen( + listenerName, + [&promise, listenerName](const LLSD& status) + { + // anything except "running" should wake up the waiting + // coroutine + auto& statsd = status["status"]; + if (statsd.asString() != "running") + { + LL_DEBUGS("lleventcoro") << listenerName + << " spotted status " << statsd + << ", throwing Stopping" << LL_ENDL; + try + { + promise.set_exception( + std::make_exception_ptr( + LLCoros::Stopping("status " + statsd.asString()))); + } + catch (const boost::fibers::promise_already_satisfied& exc) + { + LL_WARNS("lleventcoro") << listenerName + << " couldn't throw Stopping " + "because promise already set" << LL_ENDL; + } + } + // do not consume -- every listener must see status + return false; + })); LLBoundListener connection( replyPump.listen( listenerName, @@ -160,6 +204,7 @@ LLBoundListener postAndSuspendSetup(const std::string& callerName, return false; } })); + // skip the "post" part if requestPump is default-constructed if (requestPumpP) { @@ -179,7 +224,7 @@ LLBoundListener postAndSuspendSetup(const std::string& callerName, LL_DEBUGS("lleventcoro") << callerName << ": coroutine " << listenerName << " about to wait on LLEventPump " << replyPump.getName() << LL_ENDL; - return connection; + return { connection, stopper }; } } // anonymous @@ -190,15 +235,17 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ LLCoros::Promise<LLSD> promise; std::string listenerName(listenerNameForCoro()); - // Store connection into an LLTempBoundListener so we implicitly + // Store both connections into LLTempBoundListeners so we implicitly // disconnect on return from this function. - LLTempBoundListener connection = + auto connections = postAndSuspendSetup("postAndSuspend()", listenerName, promise, event, requestPump, replyPump, replyPumpNamePath); + LLTempBoundListener connection(connections.first), stopper(connections.second); // declare the future LLCoros::Future<LLSD> future = LLCoros::getFuture(promise); // calling get() on the future makes us wait for it + LLCoros::TempStatus st(STRINGIZE("waiting for " << replyPump.getPump().getName())); LLSD value(future.get()); LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName << " resuming with " << value << LL_ENDL; @@ -215,17 +262,22 @@ LLSD llcoro::postAndSuspendWithTimeout(const LLSD& event, LLCoros::Promise<LLSD> promise; std::string listenerName(listenerNameForCoro()); - // Store connection into an LLTempBoundListener so we implicitly + // Store both connections into LLTempBoundListeners so we implicitly // disconnect on return from this function. - LLTempBoundListener connection = + auto connections = postAndSuspendSetup("postAndSuspendWithTimeout()", listenerName, promise, event, requestPump, replyPump, replyPumpNamePath); + LLTempBoundListener connection(connections.first), stopper(connections.second); // declare the future LLCoros::Future<LLSD> future = LLCoros::getFuture(promise); // wait for specified timeout - boost::fibers::future_status status = - future.wait_for(std::chrono::milliseconds(long(timeout * 1000))); + boost::fibers::future_status status; + { + LLCoros::TempStatus st(STRINGIZE("waiting for " << replyPump.getPump().getName() + << " for " << timeout << "s")); + status = future.wait_for(std::chrono::milliseconds(long(timeout * 1000))); + } // if the future is NOT yet ready, return timeoutResult instead if (status == boost::fibers::future_status::timeout) { |