From a76209d099c3a18967d26e5b091f83e9ae9fd6a1 Mon Sep 17 00:00:00 2001 From: Nat Goodspeed Date: Tue, 22 Oct 2024 09:44:43 -0400 Subject: Eliminate double names for coros + "empty name is main" convention. Instead, introduce bool CoroData::isMain and test that. Use "main" for the name of the main coroutine. That eliminates the logname() method, also the llcoro::logname() free function. It also obviates the alternate CoroData constructor. Use boost::fibers::fiber::id as the LLInstanceTracker key for CoroData, instead of the coroutine name. Introduce get_CoroData(id), also getName(id). Extract static CoroData for the main coroutine to main_CoroData() so both get_CoroData() overloads can use it. Ditch unused get_CoroData(string) parameter: now get_CoroData(). Introduce LLCoros::mNameMap for lookup by name (e.g. killreq()). CoroData's constructor puts an entry into mNameMap; the destructor removes it. Since mNameMap is thread_local (unlike an LLInstanceTracker key), that theoretically permits duplicate coroutine names on different threads. Introduce mPrefixMap to help generate distinct coroutine names, instead of a single scalar. Introduce CoroData::getName(), and use it in both LLCoros::getName() overloads. CoroData::getName() appends mStatus if it's not empty. This is useful for disambiguating generic pool coroutines based on the current task. --- indra/llcommon/llcoros.h | 97 +++++++++++++++++++++++------------------------- 1 file changed, 46 insertions(+), 51 deletions(-) (limited to 'indra/llcommon/llcoros.h') diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 0291d7f1d9..662be9dcc9 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -39,39 +39,35 @@ #include #include #include +#include + +namespace llcoro +{ +class scheduler; +} /** - * Registry of named Boost.Coroutine instances - * - * The Boost.Coroutine library supports the general case of a coroutine - * accepting arbitrary parameters and yielding multiple (sets of) results. For - * such use cases, it's natural for the invoking code to retain the coroutine - * instance: the consumer repeatedly calls into the coroutine, perhaps passing - * new parameter values, prompting it to yield its next result. - * - * Our typical coroutine usage is different, though. For us, coroutines - * provide an alternative to the @c Responder pattern. Our typical coroutine - * has @c void return, invoked in fire-and-forget mode: the handler for some - * user gesture launches the coroutine and promptly returns to the main loop. - * The coroutine initiates some action that will take multiple frames (e.g. a - * capability request), waits for its result, processes it and silently steals - * away. + * Registry of named Boost.Fiber instances * - * This usage poses two (related) problems: + * When the viewer first introduced the semi-independent execution agents now + * called fibers, the term "fiber" had not yet become current, and the only + * available libraries used the term "coroutine" instead. Within the viewer we + * continue to use the term "coroutines," though at present they are actually + * boost::fibers::fiber instances. * - * # Who should own the coroutine instance? If it's simply local to the - * handler code that launches it, return from the handler will destroy the - * coroutine object, terminating the coroutine. - * # Once the coroutine terminates, in whatever way, who's responsible for - * cleaning up the coroutine object? + * Coroutines provide an alternative to the @c Responder pattern. Our typical + * coroutine has @c void return, invoked in fire-and-forget mode: the handler + * for some user gesture launches the coroutine and promptly returns to the + * main loop. The coroutine initiates some action that will take multiple + * frames (e.g. a capability request), waits for its result, processes it and + * silently steals away. * * LLCoros is a Singleton collection of currently-active coroutine instances. * Each has a name. You ask LLCoros to launch a new coroutine with a suggested * name prefix; from your prefix it generates a distinct name, registers the * new coroutine and returns the actual name. * - * The name - * can 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. */ class LL_COMMON_API LLCoros: public LLSingleton @@ -91,12 +87,8 @@ public: // llassert(LLCoros::on_main_thread_main_coro()) static bool on_main_thread_main_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; + typedef coro::id id; /// Canonical callable type typedef std::function callable_t; @@ -150,12 +142,15 @@ public: /** * From within a coroutine, look up the (tweaked) name string by which - * this coroutine is registered. Returns the empty string if not found - * (e.g. if the coroutine was launched by hand rather than using - * LLCoros::launch()). + * this coroutine is registered. */ static std::string getName(); + /** + * Given an id, return the name of that coroutine. + */ + static std::string getName(id); + /** * rethrow() is called by the thread's main fiber to propagate an * exception from any coroutine into the main fiber, where it can engage @@ -169,13 +164,6 @@ public: */ void rethrow(); - /** - * 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 @@ -187,7 +175,7 @@ public: void printActiveCoroutines(const std::string& when=std::string()); /// get the current coro::id for those who really really care - static coro::id get_self(); + static id get_self(); /** * Most coroutines, most of the time, don't "consume" the events for which @@ -236,6 +224,7 @@ public: setStatus(status); } TempStatus(const TempStatus&) = delete; + TempStatus& operator=(const TempStatus&) = delete; ~TempStatus() { setStatus(mOldStatus); @@ -331,10 +320,14 @@ public: using local_ptr = boost::fibers::fiber_specific_ptr; private: + friend class llcoro::scheduler; + std::string generateDistinctName(const std::string& prefix) const; void toplevel(std::string name, callable_t callable); struct CoroData; - static CoroData& get_CoroData(const std::string& caller); + static CoroData& get_CoroData(); + static CoroData& get_CoroData(id); + static CoroData& main_CoroData(); void saveException(const std::string& name, std::exception_ptr exc); LLTempBoundListener mConn; @@ -355,13 +348,18 @@ private: S32 mStackSize; // coroutine-local storage, as it were: one per coro we track - struct CoroData: public LLInstanceTracker + struct CoroData: public LLInstanceTracker { + using super = LLInstanceTracker; + CoroData(const std::string& name); - CoroData(int n); + ~CoroData(); + std::string getName() const; + + bool isMain{ false }; // tweaked name of the current coroutine - const std::string mName; + std::string mName; // set_consuming() state -- don't consume events unless specifically directed bool mConsuming{ false }; // killed by which coroutine @@ -375,14 +373,11 @@ private: // because it's a member of an LLSingleton, and we rely on it being // cleaned up in proper dependency order. local_ptr mCurrent; -}; -namespace llcoro -{ - -inline -std::string logname() { return LLCoros::logname(); } - -} // llcoro + // ensure name uniqueness + static thread_local std::unordered_map mPrefixMap; + // lookup by name + static thread_local std::unordered_map mNameMap; +}; #endif /* ! defined(LL_LLCOROS_H) */ -- cgit v1.2.3