From 66981fab0b3c8dcc3a031d50710a2b24ec6b0603 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 10 May 2018 21:46:07 -0400 Subject: SL-793: Use Boost.Fiber instead of the "dcoroutine" library. 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). --- indra/llcommon/llcoros.h | 184 +++++++++++++---------------------------------- 1 file changed, 48 insertions(+), 136 deletions(-) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index c551413811..678633497d 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -29,22 +29,13 @@ #if ! defined(LL_LLCOROS_H) #define LL_LLCOROS_H -#include -#include +#include +#include +#include #include "llsingleton.h" #include #include -#include -#include #include -#include -#include "llcoro_get_id.h" // for friend declaration - -// forward-declare helper class -namespace llcoro -{ -class Suspending; -} /** * Registry of named Boost.Coroutine instances @@ -76,19 +67,20 @@ class Suspending; * name prefix; from your prefix it generates a distinct name, registers the * new coroutine and returns the actual name. * - * The name can be used to kill off the coroutine prematurely, if needed. It - * can also provide diagnostic info: we can look up the name of the + * The name + * can provide diagnostic info: we can look up the name of the * currently-running coroutine. - * - * Finally, the next frame ("mainloop" event) after the coroutine terminates, - * LLCoros will notice its demise and destroy it. */ class LL_COMMON_API LLCoros: public LLSingleton { LLSINGLETON(LLCoros); public: - /// Canonical boost::dcoroutines::coroutine signature we use - typedef boost::dcoroutines::coroutine coro; + /// The viewer's use of the term "coroutine" became deeply embedded before + /// the industry term "fiber" emerged to distinguish userland threads from + /// simpler, more transient kinds of coroutines. Semantically they've + /// always been fibers. But at this point in history, we're pretty much + /// stuck with the term "coroutine." + typedef boost::fibers::fiber coro; /// Canonical callable type typedef boost::function callable_t; @@ -119,10 +111,10 @@ public: * DEV-32777 comments for an explanation. * * Pass a nullary callable. It works to directly pass a nullary free - * function (or static method); for all other cases use boost::bind(). Of - * course, for a non-static class method, the first parameter must be the - * class instance. Any other parameters should be passed via the bind() - * expression. + * function (or static method); for other cases use a lambda expression, + * std::bind() or boost::bind(). Of course, for a non-static class method, + * the first parameter must be the class instance. Any other parameters + * should be passed via the enclosing expression. * * launch() tweaks the suggested name so it won't collide with any * existing coroutine instance, creates the coroutine instance, registers @@ -138,7 +130,7 @@ public: * one prematurely. Returns @c true if the specified name was found and * still running at the time. */ - bool kill(const std::string& name); +// bool kill(const std::string& name); /** * From within a coroutine, look up the (tweaked) name string by which @@ -148,14 +140,18 @@ public: */ std::string getName() const; - /// for delayed initialization + /** + * For delayed initialization. To be clear, this will only affect + * coroutines launched @em after this point. The underlying facility + * provides no way to alter the stack size of any running coroutine. + */ void setStackSize(S32 stacksize); /// for delayed initialization void printActiveCoroutines(); - /// get the current coro::self& for those who really really care - static coro::self& get_self(); + /// get the current coro::id for those who really really care + static coro::id get_self(); /** * Most coroutines, most of the time, don't "consume" the events for which @@ -190,141 +186,57 @@ public: }; /** - * Please do NOT directly use boost::dcoroutines::future! It is essential - * to maintain the "current" coroutine at every context switch. This - * Future wraps the essential boost::dcoroutines::future functionality - * with that maintenance. + * Aliases for promise and future. An older underlying future implementation + * required us to wrap future; that's no longer needed. However -- if it's + * important to restore kill() functionality, we might need to provide a + * proxy, so continue using the aliases. */ template - class Future; + using Promise = boost::fibers::promise; + template + using Future = boost::fibers::future; + template + static Future getFuture(Promise& promise) { return promise.get_future(); } + + /// for data local to each running coroutine + template + using local_ptr = boost::fibers::fiber_specific_ptr; private: - friend class llcoro::Suspending; - friend llcoro::id llcoro::get_id(); std::string generateDistinctName(const std::string& prefix) const; - bool cleanup(const LLSD&); + void toplevel(const std::string& name, const callable_t& callable); struct CoroData; - static void no_cleanup(CoroData*); #if LL_WINDOWS static void winlevel(const callable_t& callable); #endif - static void toplevel(coro::self& self, CoroData* data, const callable_t& callable); - static CoroData& get_CoroData(const std::string& caller); + CoroData& get_CoroData(const std::string& caller); + const CoroData& get_CoroData(const std::string& caller) const; S32 mStackSize; // coroutine-local storage, as it were: one per coro we track struct CoroData { - CoroData(CoroData* prev, const std::string& name, - const callable_t& callable, S32 stacksize); + CoroData(const std::string& name); - // The boost::dcoroutines library supports asymmetric coroutines. Every - // time we context switch out of a coroutine, we pass control to the - // previously-active one (or to the non-coroutine stack owned by the - // thread). So our management of the "current" coroutine must be able to - // restore the previous value when we're about to switch away. - CoroData* mPrev; // tweaked name of the current coroutine const std::string mName; - // the actual coroutine instance - LLCoros::coro mCoro; // set_consuming() state bool mConsuming; - // When the dcoroutine library calls a top-level callable, it implicitly - // passes coro::self& as the first parameter. All our consumer code used - // to explicitly pass coro::self& down through all levels of call stack, - // because at the leaf level we need it for context-switching. But since - // coroutines are based on cooperative switching, we can cause the - // top-level entry point to stash a pointer to the currently-running - // coroutine, and manage it appropriately as we switch out and back in. - // That eliminates the need to pass it as an explicit parameter down - // through every level, which is unfortunately viral in nature. Finding it - // implicitly rather than explicitly allows minor maintenance in which a - // leaf-level function adds a new async I/O call that suspends the calling - // coroutine, WITHOUT having to propagate coro::self& through every - // function signature down to that point -- and of course through every - // other caller of every such function. - LLCoros::coro::self* mSelf; F64 mCreationTime; // since epoch }; typedef boost::ptr_map CoroMap; CoroMap mCoros; - // Identify the current coroutine's CoroData. Use a little helper class so - // a caller can either use a temporary instance, or instantiate a named - // variable and access it multiple times. - class Current - { - public: - Current(); - - operator LLCoros::CoroData*() { return get(); } - LLCoros::CoroData* operator->() { return get(); } - LLCoros::CoroData* get() { return mCurrent->get(); } - void reset(LLCoros::CoroData* ptr) { mCurrent->reset(ptr); } - - private: - boost::thread_specific_ptr* mCurrent; - }; -}; - -namespace llcoro -{ + // Identify the current coroutine's CoroData. This local_ptr isn't static + // because it's a member of an LLSingleton, and we rely on it being + // cleaned up in proper dependency order. + // As each coroutine terminates, use our custom cleanup function to remove + // the corresponding entry from mCoros. + local_ptr mCurrent{delete_CoroData}; -/// Instantiate one of these in a block surrounding any leaf point when -/// control literally switches away from this coroutine. -class Suspending: boost::noncopyable -{ -public: - Suspending(); - ~Suspending(); - -private: - LLCoros::CoroData* mSuspended; -}; - -} // namespace llcoro - -template -class LLCoros::Future -{ - typedef boost::dcoroutines::future dfuture; - -public: - Future(): - mFuture(get_self()) - {} - - typedef typename boost::dcoroutines::make_callback_result::type callback_t; - - callback_t make_callback() - { - return boost::dcoroutines::make_callback(mFuture); - } - -#ifndef LL_LINUX - explicit -#endif - operator bool() const - { - return bool(mFuture); - } - - bool operator!() const - { - return ! mFuture; - } - - T get() - { - // instantiate Suspending to manage the "current" coroutine - llcoro::Suspending suspended; - return *mFuture; - } - -private: - dfuture mFuture; + // Cleanup function for each fiber's instance of mCurrent. + static void delete_CoroData(CoroData* cdptr); }; #endif /* ! defined(LL_LLCOROS_H) */ -- cgit v1.2.3 From d7c2e4a77bed665d7ab626d9955c35db8c318e95 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Wed, 11 Sep 2019 09:33:07 -0400 Subject: DRTVWR-476: Add Sync class to help with stepwise coroutine tests. Sync is specifically intended for test programs. It is based on an LLScalarCond. The idea is that each of two coroutines can watch for the other to get a chance to run, indicated by incrementing the wrapped int and notifying the wrapped condition_variable. This is less hand-wavy than calling llcoro::suspend() and hoping that the other routine will have had a chance to run. Use Sync in lleventcoro_test.cpp. Also refactor lleventcoro_test.cpp so that instead of a collection of static data requiring a clear() call at start of each individual test function, the relevant data is all part of the test_data struct common to all test functions. Make the helper coroutine functions members of test_data too. Introduce llcoro::logname(), a convenience function to log the name of the currently executing coroutine or "main" if in the thread's main coroutine. --- indra/llcommon/llcoros.h | 13 +++++++++++++ 1 file changed, 13 insertions(+) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 678633497d..dedb6c8eca 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -239,4 +239,17 @@ private: static void delete_CoroData(CoroData* cdptr); }; +namespace llcoro +{ + +inline +std::string logname() +{ + static std::string main("main"); + std::string name(LLCoros::instance().getName()); + return name.empty()? main : name; +} + +} // llcoro + #endif /* ! defined(LL_LLCOROS_H) */ -- cgit v1.2.3 From 28a54c2f7b25b9fda763c51746701f0cd21c8018 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 22 Oct 2019 16:49:29 -0400 Subject: DRTVWR-476: Infrastructure to help manage long-lived coroutines. Introduce LLCoros::Stop exception, with subclasses Stopping, Stopped and Shutdown. Add LLCoros::checkStop(), intended to be called periodically by any coroutine with nontrivial lifespan. It checks the LLApp status and, unless isRunning(), throws one of these new exceptions. Make LLCoros::toplevel() catch Stop specially and log forcible coroutine termination. Now that LLApp status matters even in a test program, introduce a trivial LLTestApp subclass whose sole function is to make isRunning() true. (LLApp::setStatus() is protected: only a subclass can call it.) Add LLTestApp instances to lleventcoro_test.cpp and lllogin_test.cpp. Make LLCoros::toplevel() accept parameters by value rather than by const reference so we can continue using them even after context switches. Make private LLCoros::get_CoroData() static. Given that we've observed some coroutines living past LLCoros destruction, making the caller call LLCoros::instance() is more dangerous than encapsulating it within a static method -- since the encapsulated call can check LLCoros::wasDeleted() first and do something reasonable instead. This also eliminates the need for both a const and non-const overload. Defend LLCoros::delete_CoroData() (cleanup function for fiber_specific_ptr for CoroData, implicitly called after coroutine termination) against calls after ~LLCoros(). Add a status string to coroutine-local data, with LLCoro::setStatus(), getStatus() and RAII class TempStatus. Add an optional 'when' string argument to LLCoros::printActiveCoroutines(). Make ~LLCoros() print the coroutines still active at destruction. --- indra/llcommon/llcoros.h | 66 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 5 deletions(-) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index dedb6c8eca..de7b691284 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -29,6 +29,7 @@ #if ! defined(LL_LLCOROS_H) #define LL_LLCOROS_H +#include "llexception.h" #include #include #include @@ -74,6 +75,7 @@ class LL_COMMON_API LLCoros: public LLSingleton { LLSINGLETON(LLCoros); + ~LLCoros(); public: /// The viewer's use of the term "coroutine" became deeply embedded before /// the industry term "fiber" emerged to distinguish userland threads from @@ -147,8 +149,8 @@ public: */ void setStackSize(S32 stacksize); - /// for delayed initialization - void printActiveCoroutines(); + /// diagnostic + void printActiveCoroutines(const std::string& when=std::string()); /// get the current coro::id for those who really really care static coro::id get_self(); @@ -176,6 +178,7 @@ public: { set_consuming(consuming); } + OverrideConsuming(const OverrideConsuming&) = delete; ~OverrideConsuming() { set_consuming(mPrevConsuming); @@ -185,6 +188,58 @@ public: bool mPrevConsuming; }; + /// set string coroutine status for diagnostic purposes + static void setStatus(const std::string& status); + static std::string getStatus(); + + /// RAII control of status + class TempStatus + { + public: + TempStatus(const std::string& status): + mOldStatus(getStatus()) + { + setStatus(status); + } + TempStatus(const TempStatus&) = delete; + ~TempStatus() + { + setStatus(mOldStatus); + } + + private: + std::string mOldStatus; + }; + + /// thrown by checkStop() + struct Stop: public LLContinueError + { + Stop(const std::string& what): LLContinueError(what) {} + }; + + /// early stages + struct Stopping: public Stop + { + Stopping(const std::string& what): Stop(what) {} + }; + + /// cleaning up + struct Stopped: public Stop + { + Stopped(const std::string& what): Stop(what) {} + }; + + /// cleaned up -- not much survives! + struct Shutdown: public Stop + { + Shutdown(const std::string& what): Stop(what) {} + }; + + /// Call this intermittently if there's a chance your coroutine might + /// continue running into application shutdown. Throws Stop if LLCoros has + /// been cleaned up. + static void checkStop(); + /** * Aliases for promise and future. An older underlying future implementation * required us to wrap future; that's no longer needed. However -- if it's @@ -204,13 +259,12 @@ public: private: std::string generateDistinctName(const std::string& prefix) const; - void toplevel(const std::string& name, const callable_t& callable); + void toplevel(std::string name, callable_t callable); struct CoroData; #if LL_WINDOWS static void winlevel(const callable_t& callable); #endif - CoroData& get_CoroData(const std::string& caller); - const CoroData& get_CoroData(const std::string& caller) const; + static CoroData& get_CoroData(const std::string& caller); S32 mStackSize; @@ -223,6 +277,8 @@ private: const std::string mName; // set_consuming() state bool mConsuming; + // setStatus() state + std::string mStatus; F64 mCreationTime; // since epoch }; typedef boost::ptr_map CoroMap; -- cgit v1.2.3 From cbf146f2b3fc255bc83f2b01101dc29658bea6ea Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 24 Oct 2019 12:54:38 -0400 Subject: DRTVWR-476: Pump coroutines a few more times when we start quitting. 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. --- indra/llcommon/llcoros.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index de7b691284..171d1ebd2a 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -34,6 +34,7 @@ #include #include #include "llsingleton.h" +#include "llevents.h" #include #include #include @@ -284,6 +285,8 @@ private: typedef boost::ptr_map CoroMap; CoroMap mCoros; + LLTempBoundListener mAppListener; + // Identify the current coroutine's CoroData. This local_ptr isn't static // because it's a member of an LLSingleton, and we rely on it being // cleaned up in proper dependency order. -- cgit v1.2.3 From 26c8ccfc06bc9334c9a4d0d027e83ad0b1b92a86 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 24 Oct 2019 16:05:37 -0400 Subject: DRTVWR-476: Back out changeset 40c0c6a8407d ("final" LLApp listener) --- indra/llcommon/llcoros.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 171d1ebd2a..de7b691284 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -34,7 +34,6 @@ #include #include #include "llsingleton.h" -#include "llevents.h" #include #include #include @@ -285,8 +284,6 @@ private: typedef boost::ptr_map CoroMap; CoroMap mCoros; - LLTempBoundListener mAppListener; - // Identify the current coroutine's CoroData. This local_ptr isn't static // because it's a member of an LLSingleton, and we rely on it being // cleaned up in proper dependency order. -- cgit v1.2.3 From 9008124d352a195616bc8e7b88b048f479cdc4c2 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 25 Oct 2019 06:55:45 -0400 Subject: DRTVWR-476: Keep coroutine-local data on toplevel()'s stack frame. Instead of heap-allocating a CoroData instance per coroutine, storing the pointer in a ptr_map and deleting it from the ptr_map once the fiber_specific_ptr for that coroutine is cleaned up -- just declare a stack instance on the top-level stack frame, the simplest C++ lifespan management. Derive CoroData from LLInstanceTracker to detect potential name collisions and to enumerate instances. Continue registering each coroutine's CoroData instance in our fiber_specific_ptr, but use a no-op deleter function. Make ~LLCoros() directly pump the fiber scheduler a few times, instead of having a special "LLApp" listener. --- indra/llcommon/llcoros.h | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index de7b691284..7b3420cc8f 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -34,7 +34,7 @@ #include #include #include "llsingleton.h" -#include +#include "llinstancetracker.h" #include #include @@ -269,7 +269,7 @@ private: S32 mStackSize; // coroutine-local storage, as it were: one per coro we track - struct CoroData + struct CoroData: public LLInstanceTracker { CoroData(const std::string& name); @@ -281,18 +281,11 @@ private: std::string mStatus; F64 mCreationTime; // since epoch }; - typedef boost::ptr_map CoroMap; - CoroMap mCoros; // Identify the current coroutine's CoroData. This local_ptr isn't static // because it's a member of an LLSingleton, and we rely on it being // cleaned up in proper dependency order. - // As each coroutine terminates, use our custom cleanup function to remove - // the corresponding entry from mCoros. - local_ptr mCurrent{delete_CoroData}; - - // Cleanup function for each fiber's instance of mCurrent. - static void delete_CoroData(CoroData* cdptr); + local_ptr mCurrent; }; namespace llcoro -- cgit v1.2.3 From 2a56ab44360aa49bd0df9281efc8a8a97a510013 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Fri, 22 Nov 2019 11:58:27 -0500 Subject: DRTVWR-476, SL-12197: Don't throw Stopping from main coroutine. 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. --- indra/llcommon/llcoros.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 7b3420cc8f..2e4cd8ccad 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -140,7 +140,7 @@ public: * (e.g. if the coroutine was launched by hand rather than using * LLCoros::launch()). */ - std::string getName() const; + static std::string getName(); /** * For delayed initialization. To be clear, this will only affect @@ -295,7 +295,7 @@ inline std::string logname() { static std::string main("main"); - std::string name(LLCoros::instance().getName()); + std::string name(LLCoros::getName()); return name.empty()? main : name; } -- cgit v1.2.3 From 39e7b48317e3f0fe37d7398099ab1e38b97963bf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 10 Dec 2019 10:56:24 -0500 Subject: DRTVWR-476: Make llcoro::logname() distinguish different threads. Actually, introduce static LLCoros::logname() and make the namespaced free function an alias for that. Because CoroData is a subclass of LLInstanceTracker with a key, every instance requires a distinct key. That conflicts with our "getName() returns empty string for default coroutine on thread" convention. Introduce a new CoroData constructor, specifically for the default coroutine on each thread, that initializes the getName() name to empty string while providing a distinct "mainN" key. Make get_CoroData() use that new constructor for its thread_local instance, passing an atomic incremented each time we initialize one for a new thread. Then LLCoros::logname() returns either the getName() name or the key. --- indra/llcommon/llcoros.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 2e4cd8ccad..95859198d4 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -142,6 +142,13 @@ public: */ static std::string getName(); + /** + * This variation returns a name suitable for log messages: the explicit + * name for an explicitly-launched coroutine, or "mainN" for the default + * coroutine on a thread. + */ + static std::string logname(); + /** * For delayed initialization. To be clear, this will only affect * coroutines launched @em after this point. The underlying facility @@ -272,6 +279,7 @@ private: struct CoroData: public LLInstanceTracker { CoroData(const std::string& name); + CoroData(int n); // tweaked name of the current coroutine const std::string mName; @@ -292,12 +300,7 @@ namespace llcoro { inline -std::string logname() -{ - static std::string main("main"); - std::string name(LLCoros::getName()); - return name.empty()? main : name; -} +std::string logname() { return LLCoros::logname(); } } // llcoro -- cgit v1.2.3 From 98dfba0d2f24aeb92e023df9d48b23fef8253024 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Thu, 14 May 2020 14:51:52 -0400 Subject: DRTVWR-476: Wrap boost::fibers::mutex et al. with LLCoros aliases. Specifically: LLCoros::Mutex means boost::fibers::mutex LLCoros::LockType means std::unique_lock LLCoros::ConditionVariable means boost::fibers::condition_variable LLCoros::cv_status means boost::fibers::cv_status So as not to drag in all of boost::fibers::mutex.hpp or condition_variable.hpp for each consumer of llcoros.h, instead #define LLCOROS_MUTEX_HEADER and LLCOROS_CONDVAR_HEADER. Those who need them can #include the relevant macro. Update llcond.h and llthreadsafequeue.h accordingly. --- indra/llcommon/llcoros.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 95859198d4..d49a6e939c 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -33,11 +33,24 @@ #include #include #include +#include "mutex.h" #include "llsingleton.h" #include "llinstancetracker.h" #include #include +// e.g. #include LLCOROS_MUTEX_HEADER +#define LLCOROS_MUTEX_HEADER +#define LLCOROS_CONDVAR_HEADER + +namespace boost { + namespace fibers { + class mutex; + enum class cv_status; + class condition_variable; + } +} + /** * Registry of named Boost.Coroutine instances * @@ -260,6 +273,12 @@ public: template static Future getFuture(Promise& promise) { return promise.get_future(); } + // use mutex, lock, condition_variable suitable for coroutines + using Mutex = boost::fibers::mutex; + using LockType = std::unique_lock; + using cv_status = boost::fibers::cv_status; + using ConditionVariable = boost::fibers::condition_variable; + /// for data local to each running coroutine template using local_ptr = boost::fibers::fiber_specific_ptr; -- cgit v1.2.3 From 87da08b1f49a600b0e1993b31e46b1808aa8fecf Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 7 Jul 2020 14:48:36 -0400 Subject: DRTVWR-476, SL-13555: Don't crash if user closes viewer during login. Ever since February 2010, the body of the login coroutine function has been enclosed in try/catch (...), with an llerrs message to try to crash more informatively than the runtime's unhandled-exception termination. Over the years this evolved to LL_ERRS and then to CRASH_ON_UNHANDLED_EXCEPTION. This persisted despite the August 2016 addition of generic catch clauses in the LLCoros::toplevel() function to serve the same purpose, and despite the subsequent introduction of the LLCoros::Stop family of exceptions to deliberately throw into waiting coroutines on viewer shutdown. That's exactly what was happening. When the user closed the viewer while waiting for the response from login.cgi, the waiting operation threw LLCoros::Stopping, which was caught by that CRASH_ON_UNHANDLED_EXCEPTION, which crashed the viewer with LL_ERRS rather than propagating up to the toplevel() and cleanly terminating the coroutine. Change CRASH_ON_UNHANDLED_EXCEPTION() to LOG_UNHANDLED_EXCEPTION() and re-throw so toplevel() can handle. --- indra/llcommon/llcoros.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index d49a6e939c..38c2356c99 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -232,6 +232,11 @@ public: }; /// thrown by checkStop() + // It may sound ironic that Stop is derived from LLContinueError, but the + // point is that LLContinueError is the category of exception that should + // not immediately crash the viewer. Stop and its subclasses are to notify + // coroutines that the viewer intends to shut down. The expected response + // is to terminate the coroutine, rather than abort the viewer. struct Stop: public LLContinueError { Stop(const std::string& what): LLContinueError(what) {} -- cgit v1.2.3