diff options
author | Nat Goodspeed <nat@lindenlab.com> | 2024-09-25 11:56:44 -0400 |
---|---|---|
committer | Nat Goodspeed <nat@lindenlab.com> | 2024-09-25 11:56:44 -0400 |
commit | 55df7328c6f8c864ea309c57d73e791e079b3c2c (patch) | |
tree | cb4cba5e51d9649ba229c4eefeaafe783b9250e2 /indra/llcommon/llcoros.h | |
parent | 4b2b94f4864f2e2e7d76f4f17b2d58bb959b3edb (diff) | |
parent | 86d2fb93b73d2689104c564ec859be7f83416691 (diff) |
Merge branch 'develop' into marchcat/xcode-16
Diffstat (limited to 'indra/llcommon/llcoros.h')
-rw-r--r-- | indra/llcommon/llcoros.h | 108 |
1 files changed, 60 insertions, 48 deletions
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index c3820ae987..0291d7f1d9 100644 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -29,30 +29,16 @@ #if ! defined(LL_LLCOROS_H) #define LL_LLCOROS_H +#include "llcoromutex.h" +#include "llevents.h" #include "llexception.h" -#include <boost/fiber/fss.hpp> -#include <boost/fiber/future/future.hpp> -#include <boost/fiber/future/promise.hpp> -#include <boost/fiber/recursive_mutex.hpp> -#include "mutex.h" -#include "llsingleton.h" #include "llinstancetracker.h" -#include <boost/function.hpp> -#include <string> +#include "llsingleton.h" +#include <boost/fiber/fss.hpp> #include <exception> +#include <functional> #include <queue> - -// e.g. #include LLCOROS_MUTEX_HEADER -#define LLCOROS_MUTEX_HEADER <boost/fiber/mutex.hpp> -#define LLCOROS_CONDVAR_HEADER <boost/fiber/condition_variable.hpp> - -namespace boost { - namespace fibers { - class mutex; - enum class cv_status; - class condition_variable; - } -} +#include <string> /** * Registry of named Boost.Coroutine instances @@ -112,7 +98,7 @@ public: /// stuck with the term "coroutine." typedef boost::fibers::fiber coro; /// Canonical callable type - typedef boost::function<void()> callable_t; + typedef std::function<void()> callable_t; /** * Create and start running a new coroutine with specified name. The name @@ -154,13 +140,13 @@ public: std::string launch(const std::string& prefix, const callable_t& callable); /** - * Abort a running coroutine by name. Normally, when a coroutine either + * Ask the named coroutine to abort. Normally, when a coroutine either * runs to completion or terminates with an exception, LLCoros quietly * cleans it up. This is for use only when you must explicitly interrupt * one prematurely. Returns @c true if the specified name was found and * still running at the time. */ -// bool kill(const std::string& name); + bool killreq(const std::string& name); /** * From within a coroutine, look up the (tweaked) name string by which @@ -262,15 +248,21 @@ 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. + // not immediately crash the viewer. Stop and its subclasses are to tell + // coroutines to terminate, e.g. because the viewer is shutting down. We + // do not want any such exception to crash the viewer. struct Stop: public LLContinueError { Stop(const std::string& what): LLContinueError(what) {} }; - /// early stages + /// someone wants to kill this specific coroutine + struct Killed: public Stop + { + Killed(const std::string& what): Stop(what) {} + }; + + /// early shutdown stages struct Stopping: public Stop { Stopping(const std::string& what): Stop(what) {} @@ -289,34 +281,50 @@ public: }; /// 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(); + /// still be running at application shutdown. Throws one of the Stop + /// subclasses if the caller needs to terminate. Pass a cleanup function + /// if you need to execute that cleanup before terminating. + /// Of course, if your cleanup function throws, that will be the exception + /// propagated by checkStop(). + static void checkStop(callable_t cleanup={}); + + /// Call getStopListener() at the source end of a queue, promise or other + /// resource on which coroutines will wait, so that shutdown can wake up + /// consuming coroutines. @a caller should distinguish who's calling. The + /// passed @a cleanup function must close the queue, break the promise or + /// otherwise cause waiting consumers to wake up in an abnormal way. It's + /// advisable to store the returned LLBoundListener in an + /// LLTempBoundListener, or otherwise arrange to disconnect it. + static LLBoundListener getStopListener(const std::string& caller, LLVoidListener cleanup); + + /// This getStopListener() overload is like the two-argument one, for use + /// when we know the name of the only coroutine that will wait on the + /// resource in question. Pass @a consumer as the empty string if the + /// consumer coroutine is the same as the calling coroutine. Unlike the + /// two-argument getStopListener(), this one also responds to + /// killreq(target). + static LLBoundListener getStopListener(const std::string& caller, + const std::string& consumer, + LLVoidListener cleanup); /** - * 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. + * LLCoros aliases for promise and future, for backwards compatibility. + * These have been hoisted out to the llcoro namespace. */ template <typename T> - using Promise = boost::fibers::promise<T>; + using Promise = llcoro::Promise<T>; template <typename T> - using Future = boost::fibers::future<T>; + using Future = llcoro::Future<T>; template <typename T> static Future<T> getFuture(Promise<T>& promise) { return promise.get_future(); } // use mutex, lock, condition_variable suitable for coroutines - using Mutex = boost::fibers::mutex; - using RMutex = boost::fibers::recursive_mutex; - // With C++17, LockType is deprecated: at this point we can directly - // declare 'std::unique_lock lk(some_mutex)' without explicitly stating - // the mutex type. Sadly, making LockType an alias template for - // std::unique_lock doesn't work the same way: Class Template Argument - // Deduction only works for class templates, not alias templates. - using LockType = std::unique_lock<Mutex>; - using cv_status = boost::fibers::cv_status; - using ConditionVariable = boost::fibers::condition_variable; + using Mutex = llcoro::Mutex; + using RMutex = llcoro::RMutex; + // LockType is deprecated; see llcoromutex.h + using LockType = llcoro::LockType; + using cv_status = llcoro::cv_status; + using ConditionVariable = llcoro::ConditionVariable; /// for data local to each running coroutine template <typename T> @@ -329,6 +337,8 @@ private: static CoroData& get_CoroData(const std::string& caller); void saveException(const std::string& name, std::exception_ptr exc); + LLTempBoundListener mConn; + struct ExceptionData { ExceptionData(const std::string& nm, std::exception_ptr exc): @@ -352,8 +362,10 @@ private: // tweaked name of the current coroutine const std::string mName; - // set_consuming() state - bool mConsuming; + // set_consuming() state -- don't consume events unless specifically directed + bool mConsuming{ false }; + // killed by which coroutine + std::string mKilledBy; // setStatus() state std::string mStatus; F64 mCreationTime; // since epoch |