summaryrefslogtreecommitdiff
path: root/indra/llcommon/lleventcoro.cpp
AgeCommit message (Collapse)Author
2020-06-26DRTVWR-476, SL-13512: Fix flawed fix for former failure.Nat Goodspeed
Specifically, llcoro::suspendUntilTimeout() is definitely called concurrently by multiple coroutines. New code that instantiates a local LLEventStream must allow the name to be tweaked for uniqueness.
2020-06-25DRTVWR-476, SL-13512: Make suspendUntilTimeout() notice shutdown.Nat Goodspeed
Specifically, the shutdown crash reported in SL-13512 was due to LLExperienceCache::idleCoro() looping on suspendUntilTimeout(), failing to notice in its slumbers that the viewer was shutting down around it. Make suspendUntilTimeout() internally call suspendUntilEventOnWithTimeout(), which already listens for "LLApp" state-change events and throws Stopping when LLApp enters its shutdown sequence.
2020-03-25DRTVWR-476, SL-12197: Don't throw Stopping from main coroutine.Nat Goodspeed
The new LLCoros::Stop exception is intended to terminate long-lived coroutines -- not interrupt mainstream shutdown processing. Only throw it on an explicitly-launched coroutine. Make LLCoros::getName() (used by the above test) static. As with other LLCoros methods, it might be called after the LLCoros LLSingleton instance has been deleted. Requiring the caller to call instance() implies a possible need to also call wasDeleted(). Encapsulate that nuance into a static method instead.
2020-03-25DRTVWR-476: Back out changeset 40c0c6a8407d ("final" LLApp listener)Nat Goodspeed
2020-03-25DRTVWR-476: Pump coroutines a few more times when we start quitting.Nat Goodspeed
By the time "LLApp" listeners are notified that the app is quitting, the mainloop is no longer running. Even though those listeners do things like close work queues and inject exceptions into pending promises, any coroutines waiting on those resources must regain control before they can notice and shut down properly. Add a final "LLApp" listener that resumes ready coroutines a few more times. Make sure every other "LLApp" listener is positioned before that new one.
2020-03-25DRTVWR-476: Don't name the caught exceptionNat Goodspeed
unless we're going to reference it.
2020-03-25DRTVWR-476: Terminate long-lived coroutines to avoid shutdown crash.Nat Goodspeed
Add LLCoros::TempStatus instances around known suspension points so printActiveCoroutines() can report what each suspended coroutine is waiting for. Similarly, sprinkle checkStop() calls at known suspension points. Make LLApp::setStatus() post an event to a new LLEventPump "LLApp" with a string corresponding to the status value being set, but only until ~LLEventPumps() -- since setStatus() also gets called very late in the application's lifetime. Make postAndSuspendSetup() (used by postAndSuspend(), suspendUntilEventOn(), postAndSuspendWithTimeout(), suspendUntilEventOnWithTimeout()) add a listener on the new "LLApp" LLEventPump that pushes the new LLCoros::Stopping exception to the coroutine waiting on the LLCoros::Promise. Make it return the new LLBoundListener along with the previous one. Accordingly, make postAndSuspend() and postAndSuspendWithTimeout() store the new LLBoundListener returned by postAndSuspendSetup() in a LLTempBoundListener (as with the previous one) so it will automatically disconnect once the wait is over. Make each LLCoprocedurePool instance listen on "LLApp" with a listener that closes the queue on which new work items are dispatched. Closing the queue causes the waiting dispatch coroutine to terminate. Store the connection in an LLTempBoundListener on the LLCoprocedurePool so it will disconnect automatically on destruction. Refactor the loop in coprocedureInvokerCoro() to instantiate TempStatus around the suspending call. Change a couple spammy LL_INFOS() calls to LL_DEBUGS(). Give all logging calls in that module a "CoProcMgr" tag to make it straightforward to re-enable the LL_DEBUGS() calls as desired.
2020-03-25DRTVWR-476: Seems gcc 4.8 forbids brace-init of reference vars.Nat Goodspeed
Although this is legal, apparently there was a bug in the C++11 standard (to which gcc 4.8 conforms) that was subsequently fixed in the standard (and thus in gcc 4.9). Thanks Henri Beauchamp.
2020-03-25DRTVWR-476: Validate LLEventPumpOrPumpName replyPumpNat Goodspeed
passed to postAndSuspendsetup(). The requestPump is optional, and the function varies its behavior depending on whether that parameter is empty or meaningful. But it unconditionally uses the replyPump. Passing an empty LLEventPumpOrPumpName caused mysterious crashes. Add llassert_always_msg() to make the coding error explicit in such a case. Also streamline access to meaningful requestPump and replyPump by temporarily caching the bound LLEventPump reference.
2020-03-25DRTVWR-476: Fix overflow case in llcoro::postAndSuspend().Nat Goodspeed
Actually the fix is in postAndSuspendSetup(), which affects postAndSuspend(), postAndSuspendWithTimeout(), suspendUntilEventOnWithTimeout() and suspendUntilEventOn(). By "overflow case" we mean the special circumstance in which: * the LLEventPump in question is an LLEventMailDrop, meaning its listeners eventually expect to see every post()ed value * one of the listeners is supposed to consume those values (has called LLCoros::set_consuming(true)) * post() is called more than once before that listener is resumed. The magic of postAndSuspend() (et al.) is a temporary LLCoros::Promise. The waiting coroutine calls get() on the corresponding Future, causing it to suspend (as promised) until the Promise is fulfilled. With the Boost.Fiber implementation of coroutines, fulfilling the Promise doesn't immediately resume the suspended coroutine -- it merely marks it ready to resume, next time the scheduler gets control. A second post() call before the suspended coroutine is resumed results in a second call to Promise::set_value(). But Promise is a one-shot entity. This results in a promise_already_satisfied exception. Because a second post() call during that time window is perfectly reasonable, we catch that exception and carry on. The tricky part is: when that exception is thrown, what should the listener return? Previously we were returning the listener's current consuming setting, just as when the set_value() call succeeds. But when the LLEventPump is an LLEventMailDrop, and the listener's consuming flag is true, that told LLEventMailDrop::post() that the value got through, and that it needn't bother to save it in its history queue. The net effect was to discard the value. Instead, return the listener's consuming flag only when Promise::set_value() succeeds. When it throws promise_already_satisfied, unconditionally return false. That directs LLEventMailDrop::post() to enqueue the undelivered value so that the *next* suspendUntilEventOn() call can pick it up.
2020-03-25Removed unnecessary disconnection of listener in postAndSuspendSetupBrad Kittenbrink
2020-03-25Switched LL_ERRS to LL_WARNS for case where promise is fulfilled multiple ↵Brad Kittenbrink
times by multiple events.
2020-03-25Attempt to close LLEventCoro's LLBoundListener connection when promise has ↵Brad Kittenbrink
been fulfilled.
2020-03-25Added try/catch closer to source of error so LL_ERRS fatal can be more ↵Brad Kittenbrink
useful for debugging;
2020-03-25SL-793: Use Boost.Fiber instead of the "dcoroutine" library.Nat Goodspeed
Longtime fans will remember that the "dcoroutine" library is a Google Summer of Code project by Giovanni P. Deretta. He originally called it "Boost.Coroutine," and we originally added it to our 3p-boost autobuild package as such. But when the official Boost.Coroutine library came along (with a very different API), and we still needed the API of the GSoC project, we renamed the unofficial one "dcoroutine" to allow coexistence. The "dcoroutine" library had an internal low-level API more or less analogous to Boost.Context. We later introduced an implementation of that internal API based on Boost.Context, a step towards eliminating the GSoC code in favor of official, supported Boost code. However, recent versions of Boost.Context no longer support the API on which we built the shim for "dcoroutine." We started down the path of reimplementing that shim using the current Boost.Context API -- then realized that it's time to bite the bullet and replace the "dcoroutine" API with the Boost.Fiber API, which we've been itching to do for literally years now. Naturally, most of the heavy lifting is in llcoros.{h,cpp} and lleventcoro.{h,cpp} -- which is good: the LLCoros layer abstracts away most of the differences between "dcoroutine" and Boost.Fiber. The one feature Boost.Fiber does not provide is the ability to forcibly terminate some other fiber. Accordingly, disable LLCoros::kill() and LLCoprocedureManager::shutdown(). The only known shutdown() call was in LLCoprocedurePool's destructor. We also took the opportunity to remove postAndSuspend2() and its associated machinery: FutureListener2, LLErrorEvent, errorException(), errorLog(), LLCoroEventPumps. All that dual-LLEventPump stuff was introduced at a time when the Responder pattern was king, and we assumed we'd want to listen on one LLEventPump with the success handler and on another with the error handler. We have never actually used that in practice. Remove associated tests, of course. There is one other semantic difference that necessitates patching a number of tests: with "dcoroutine," fulfilling a future IMMEDIATELY resumes the waiting coroutine. With Boost.Fiber, fulfilling a future merely marks the fiber as ready to resume next time the scheduler gets around to it. To observe the test side effects, we've inserted a number of llcoro::suspend() calls -- also in the main loop. For a long time we retained a single unit test exercising the raw "dcoroutine" API. Remove that. Eliminate llcoro_get_id.{h,cpp}, which provided llcoro::get_id(), which was a hack to emulate fiber-local variables. Since Boost.Fiber has an actual API for that, remove the hack. In fact, use (new alias) LLCoros::local_ptr for LLSingleton's dependency tracking in place of llcoro::get_id(). In CMake land, replace BOOST_COROUTINE_LIBRARY with BOOST_FIBER_LIBRARY. We don't actually use the Boost.Coroutine for anything (though there exist plausible use cases).
2020-03-25SL-11216: Add llsd::drill() function to drill into an LLSD blob.Nat Goodspeed
We include both const and non-const overloads. The latter returns LLSD&, so you can assign to the located element. In fact we already implemented the non-const logic in a less public form as storeToLLSDPath() in lleventcoro.cpp. Reimplement the latter to use the new llsd::drill() function.
2016-08-17MAINT-5011: Use LLTHROW() instead of plain BOOST_THROW_EXCEPTION().Nat Goodspeed
A level of preprocessor indirection lets us later change the implementation if desired.
2016-07-19MAINT-5011: Introduce LLException base class for viewer exceptions.Nat Goodspeed
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.
2016-05-06merge 4.0.4-release and MAINT-5974Oz Linden
2016-04-26MAINT-6336: Initialize TempBoundListener with constructorRider Linden
2016-04-26MAINT-6336: Put the timeout upstream of the suspending pump and fire the ↵Rider Linden
timeout it. Also some cleanup on LLSD construction in vivox.
2016-04-22MAINT-6336: Centralize waiting on event pump with a timeout. Shorten the ↵Rider Linden
lifespan of a timeout event pump lifespan to be no longer than necessary. Change all references to the LLEventTimer to instead uses the centralized version.
2016-04-04merge with 4.0.3-releaseOz Linden
2015-12-18MAINT-5976: Fix bug in LLCoros::set_consuming() mechanism.Nat Goodspeed
The original implementation of set_consuming() involved a bool* pointing to a local bool in VoidListener::operator()()'s stack frame. postAndSuspend() would set that bool (through the pointer) as soon as it returned from suspension. The trouble with that is that LLEventMailDrop potentially calls its new listener (fulfilling the future) immediately in the listen_impl() override -- in other words, way up at the top of postAndSuspend(), well before the code that sets the relevant bool. Instead, make the adapter formerly known as VoidListener bind the coroutine's get_consuming() value at adapter construction time (before listening on the LLEventPump), so that its operator()() has the coroutine's correct get_consuming() value to return. Eliminating the bool* makes the code both simpler AND more correct! This change makes that adapter very specific to coroutine usage. Rename it FutureListener and migrate it from lleventcoros.h into the .cpp file. Nobody else was using it anyway. Make corresponding changes to postAndSuspend2() and its WaitForEventOnHelper class -- whose name no longer corresponds to the function as it used to. Rename that one FutureListener2. The new FutureListener functionality, common to both these adapters, makes it useful to derive FutureListener2 from FutureListener. Introduce llmake(), a generic function to deduce template type arguments from function parameter types. This allows us to remove the voidlistener() and wfeoh() helper functions. Hiding VoidListener broke one of the lleventcoro_test.cpp tests. But that test was sort of a lame recap of an earlier implementation of postAndSuspend(), based on LLEventPump events. Recast that test to illustrate how to use a coroutine future to suspend a coroutine for something other than an LLEventPump. But that rubbed my nose in the fact that we MUST wrap future's context switching with proper management of the current coroutine. Introduce LLCoros::Future<T>, which wraps boost::dcoroutines::future<T>. Use LLCoros::Future<T> in postAndSuspend() and postAndSuspend2().
2015-12-16MAINT-5976: Introduce LLCoros::set_consuming(bool).Nat Goodspeed
set_consuming(true) tells each postAndSuspend() call to consume the event for which it is suspending.
2015-12-04Initial changes for Vivox/Azumarill merge. Lots of temporary code and ↵Rider Linden
conditional compile switches. Begin switch from statemachine to coroutine.
2015-11-10remove execute permission from many files that should not have itOz Linden
2015-09-18Tweek to naming postEventAndSuspend -> postAndSuspendRider Linden
2015-09-18Set consistent terminology for yield/wait -> suspend for coroutines.Rider Linden
2015-07-10MAINT-5351: Finish cleaning up messy merge from backing out backoutNat Goodspeed
2015-07-10Automated merge with file:///Users/nat/linden/maint-4952-v-t-uNat Goodspeed
2015-07-10Merge restore of more selfless changesNat Goodspeed
2015-07-10Backed out changeset bab1000e1b2d: restore 'selfless' changesNat Goodspeed
2015-07-10Merge restore of selfless changesNat Goodspeed
2015-07-10Backed out changeset 6e1fa9518747: reapply 'selfless' changesNat Goodspeed
2015-07-10MAINT-5351: Improve management of "current" coroutine information.Nat Goodspeed
Our first cut at tracking the "current" coroutine simply reset the pointer to NULL every time we context-switched away. But that strategy doesn't handle the case of coroutine A launching coroutine B. Introduce LLCoros::CoroData to track, among other things, the previous value of the current-coroutine pointer each time we switch into a coroutine. Restore THAT value when we switch back out.
2015-07-07Backout selfles merge 738255dbbfd679d9e615baab3398e5e345bbb3c5Rider Linden
2015-07-07Backed out selfless mergeRider Linden
2015-07-04MAINT-5357: Add llcoro::yield() function to yield for one frame.Nat Goodspeed
2015-07-02MAINT-5357: Introduce and populate llcoro:: namespace.Nat Goodspeed
To date, the coroutine helper functions in lleventcoro.h have been in the global namespace. Migrate them into llcoro namespace, and fix references. Specifically, LLVoidListener => llcoro::VoidListener, and voidlistener(), postAndWait(), both waitForEventOn(), postAndWait2(), errorException() and errorLog() have been moved into llcoro. Also migrate new LLCoros::get_self() and Suspending to llcoro:: namespace. While at it, I realized that -- having converted several lleventcoro.h functions from templates (for arbitrary 'self' parameter type) to ordinary functions, having moved them from lleventcoro.h to lleventcoro.cpp, we can now migrate their helpers from lleventcoro.h to lleventcoro.cpp as well. This eliminates the need for the LLEventDetail namespace; the relevant helpers are now in an anonymous namespace in the .cpp file: listenerNameForCoro(), storeToLLSDPath(), WaitForEventOnHelper and wfeoh().
2015-07-01MAINT-5351: Remove 'self' parameter from coroutine functions.Nat Goodspeed
lleventcoro_test.cpp runs clean (as modified for new API), and all the rest builds clean, but the resulting viewer is as yet untested.
2013-03-29Update Mac and Windows breakpad builds to latestGraham Madarasz
2010-08-13Change license from GPL to LGPL (version 2.1)Oz Linden
2009-06-03DEV-32777: Introduce LLCoros, an LLSingleton registry of named coroutineNat Goodspeed
instances. LLCoros::launch() intends to address three issues: - ownership of coroutine instance - cleanup of coroutine instance when it terminates - central place to twiddle MSVC optimizations to bypass DEV-32777 crash. Initially coded on Mac; will address the third bullet on Windows. Adapt listenerNameForCoro() to consult LLCoros::getName() if applicable. Change LLLogin::Impl::connect() to use LLCoros::launch(). LLCoros::getName() relies on patch to boost::coroutines::coroutine::self to introduce get_id().
2009-05-11svn merge -r113003:119136 ↵Nat Goodspeed
svn+ssh://svn.lindenlab.com/svn/linden/branches/login-api/login-api-2 svn+ssh://svn.lindenlab.com/svn/linden/branches/login-api/login-api-3