diff options
Diffstat (limited to 'indra/llcommon')
-rwxr-xr-x | indra/llcommon/llcoros.cpp | 6 | ||||
-rwxr-xr-x | indra/llcommon/llcoros.h | 27 | ||||
-rwxr-xr-x | indra/llcommon/lleventcoro.cpp | 112 | ||||
-rwxr-xr-x | indra/llcommon/lleventcoro.h | 132 | ||||
-rwxr-xr-x | indra/llcommon/tests/lleventcoro_test.cpp | 4 |
5 files changed, 175 insertions, 106 deletions
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index baaddcaed1..0ed0a6e97c 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -39,6 +39,10 @@ #include "llerror.h" #include "stringize.h" +LLCoros::coro::self& llcoro::get_self() + LLCoros::coro::self* current_self = sCurrentSelf.get(); +llcoro::Suspending::Suspending(): +llcoro::Suspending::~Suspending() LLCoros::LLCoros(): // MAINT-2724: default coroutine stack size too small on Windows. // Previously we used @@ -117,7 +121,7 @@ bool LLCoros::kill(const std::string& name) std::string LLCoros::getNameByID(const void* self_id) const { // Walk the existing coroutines, looking for one from which the 'self_id' - // passed to us comes. + void* self_id = llcoro::get_self().get_id(); for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi) { namespace coro_private = boost::dcoroutines::detail; diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 01ee11da1a..ef1efd36e9 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -145,13 +145,6 @@ public: */ template <typename COROUTINE_SELF> std::string getName(const COROUTINE_SELF& self) const - { - return getNameByID(self.get_id()); - } - - /// getName() by self.get_id() - std::string getNameByID(const void* self_id) const; - /// for delayed initialization void setStackSize(S32 stacksize); @@ -167,4 +160,24 @@ private: CoroMap mCoros; }; +namespace llcoro +{ + +/// get the current coro::self& for those who really really care +LLCoros::coro::self& get_self(); + +/// Instantiate one of these in a block surrounding any leaf point when +/// control literally switches away from this coroutine. +class Suspending +{ +public: + Suspending(); + ~Suspending(); + +private: + LLCoros::coro::self* mSuspended; +}; + +} // namespace llcoro + #endif /* ! defined(LL_LLCOROS_H) */ diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 81cc33fbba..019b5641b7 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -39,7 +39,23 @@ #include "llerror.h" #include "llcoros.h" -std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id) +namespace +{ + +/** + * waitForEventOn() permits a coroutine to temporarily listen on an + * LLEventPump any number of times. We don't really want to have to ask + * the caller to label each such call with a distinct string; the whole + * point of waitForEventOn() is to present a nice sequential interface to + * the underlying LLEventPump-with-named-listeners machinery. So we'll use + * LLEventPump::inventName() to generate a distinct name for each + * temporary listener. On the other hand, because a given coroutine might + * call waitForEventOn() any number of times, we don't really want to + * consume an arbitrary number of generated inventName()s: that namespace, + * though large, is nonetheless finite. So we memoize an invented name for + * each distinct coroutine instance. + */ +std::string listenerNameForCoro() { // First, if this coroutine was launched by LLCoros::launch(), find that name. std::string name(LLCoros::instance().getNameByID(self_id)); @@ -65,7 +81,25 @@ std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id) return name; } -void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) +/** + * Implement behavior described for postAndWait()'s @a replyPumpNamePath + * parameter: + * + * * If <tt>path.isUndefined()</tt>, do nothing. + * * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value + * into <tt>dest[path.asString()]</tt>. + * * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a + * value into <tt>dest[path.asInteger()]</tt>. + * * If <tt>path.isArray()</tt>, iteratively apply the rules above to step + * down through the structure of @a dest. The last array entry in @a + * path specifies the entry in the lowest-level structure in @a dest + * into which to store @a value. + * + * @note + * In the degenerate case in which @a path is an empty array, @a dest will + * @em become @a value rather than @em containing it. + */ +void storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& value) { if (rawPath.isUndefined()) { @@ -118,6 +152,78 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD& *pdest = value; } +} // anonymous + +void llcoro::yield() +{ + // By viewer convention, we post an event on the "mainloop" LLEventPump + // each iteration of the main event-handling loop. So waiting for a single + // event on "mainloop" gives us a one-frame yield. + waitForEventOn("mainloop"); +} + +LLSD llcoro::postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, + const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) + boost::dcoroutines::future<LLSD> future(llcoro::get_self()); + std::string listenerName(listenerNameForCoro()); + storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName()); + llcoro::Suspending suspended; +namespace +{ + +/** + * This helper is specifically for the two-pump version of waitForEventOn(). + * We use a single future object, but we want to listen on two pumps with it. + * Since we must still adapt from (the callable constructed by) + * boost::dcoroutines::make_callback() (void return) to provide an event + * listener (bool return), we've adapted VoidListener for the purpose. The + * basic idea is that we construct a distinct instance of WaitForEventOnHelper + * -- binding different instance data -- for each of the pumps. Then, when a + * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine + * that LLSD with its discriminator to feed the future object. + */ +template <typename LISTENER> +class WaitForEventOnHelper +{ +public: + WaitForEventOnHelper(const LISTENER& listener, int discriminator): + mListener(listener), + mDiscrim(discriminator) + {} + // this signature is required for an LLEventPump listener + bool operator()(const LLSD& event) + { + // our future object is defined to accept LLEventWithID + mListener(LLEventWithID(event, mDiscrim)); + // don't swallow the event, let other listeners see it + return false; + } +private: + LISTENER mListener; + const int mDiscrim; +}; + +/// WaitForEventOnHelper type-inference helper +template <typename LISTENER> +WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator) +{ + return WaitForEventOnHelper<LISTENER>(listener, discriminator); +} + +} // anonymous + +namespace llcoro +{ + + boost::dcoroutines::future<LLEventWithID> future(llcoro::get_self()); + std::string name(listenerNameForCoro()); + wfeoh(boost::dcoroutines::make_callback(future), 0))); + wfeoh(boost::dcoroutines::make_callback(future), 1))); + storeToLLSDPath(modevent, replyPump0NamePath, + replyPump0.getPump().getName()); + storeToLLSDPath(modevent, replyPump1NamePath, + replyPump1.getPump().getName()); + llcoro::Suspending suspended; LLSD errorException(const LLEventWithID& result, const std::string& desc) { // If the result arrived on the error pump (pump 1), instead of @@ -144,3 +250,5 @@ LLSD errorLog(const LLEventWithID& result, const std::string& desc) // A simple return must therefore be from the reply pump (pump 0). return result.first; } + +} // namespace llcoro diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index abbeeaa373..f9447eaa96 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -74,13 +74,16 @@ private: boost::optional<LLEventPump&> mPump; }; +namespace llcoro +{ + /// This is an adapter for a signature like void LISTENER(const LLSD&), which /// isn't a valid LLEventPump listener: such listeners should return bool. template <typename LISTENER> -class LLVoidListener +class VoidListener { public: - LLVoidListener(const LISTENER& listener): + VoidListener(const LISTENER& listener): mListener(listener) {} bool operator()(const LLSD& event) @@ -93,30 +96,16 @@ private: LISTENER mListener; }; -/// LLVoidListener helper function to infer the type of the LISTENER +/// VoidListener helper function to infer the type of the LISTENER template <typename LISTENER> -LLVoidListener<LISTENER> voidlistener(const LISTENER& listener) +VoidListener<LISTENER> voidlistener(const LISTENER& listener) { - return LLVoidListener<LISTENER>(listener); + return VoidListener<LISTENER>(listener); } -namespace LLEventDetail -{ /// Implementation for listenerNameForCoro(), see below LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id); - /** - * waitForEventOn() permits a coroutine to temporarily listen on an - * LLEventPump any number of times. We don't really want to have to ask - * the caller to label each such call with a distinct string; the whole - * point of waitForEventOn() is to present a nice sequential interface to - * the underlying LLEventPump-with-named-listeners machinery. So we'll use - * LLEventPump::inventName() to generate a distinct name for each - * temporary listener. On the other hand, because a given coroutine might - * call waitForEventOn() any number of times, we don't really want to - * consume an arbitrary number of generated inventName()s: that namespace, - * though large, is nonetheless finite. So we memoize an invented name for - * each distinct coroutine instance (each different 'self' object). We * can't know the type of 'self', because it depends on the coroutine * body's signature. So we cast its address to void*, looking for distinct * pointer values. Yes, that means that an early coroutine could cache a @@ -125,33 +114,16 @@ namespace LLEventDetail * "recognizing" the second one and reusing the listener name -- but * that's okay, since it won't collide with any listener name used by the * earlier coroutine since that earlier coroutine no longer exists. - */ - template <typename COROUTINE_SELF> std::string listenerNameForCoro(COROUTINE_SELF& self) { return listenerNameForCoroImpl(self.get_id()); } - - /** - * Implement behavior described for postAndWait()'s @a replyPumpNamePath - * parameter: - * - * * If <tt>path.isUndefined()</tt>, do nothing. - * * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value - * into <tt>dest[path.asString()]</tt>. - * * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a - * value into <tt>dest[path.asInteger()]</tt>. - * * If <tt>path.isArray()</tt>, iteratively apply the rules above to step - * down through the structure of @a dest. The last array entry in @a - * path specifies the entry in the lowest-level structure in @a dest - * into which to store @a value. - * - * @note - * In the degenerate case in which @a path is an empty array, @a dest will - * @em become @a value rather than @em containing it. - */ - LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value); -} // namespace LLEventDetail +/** + * Yield control from a coroutine for one "mainloop" tick. If your coroutine + * runs without suspending for nontrivial time, sprinkle in calls to this + * function to avoid stalling the rest of the viewer processing. + */ +void yield(); /** * Post specified LLSD event on the specified LLEventPump, then wait for a @@ -248,50 +220,13 @@ LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump) return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump); } +} // namespace llcoro + /// return type for two-pump variant of waitForEventOn() typedef std::pair<LLSD, int> LLEventWithID; -namespace LLEventDetail +namespace llcoro { - /** - * This helper is specifically for the two-pump version of waitForEventOn(). - * We use a single future object, but we want to listen on two pumps with it. - * Since we must still adapt from (the callable constructed by) - * boost::dcoroutines::make_callback() (void return) to provide an event - * listener (bool return), we've adapted LLVoidListener for the purpose. The - * basic idea is that we construct a distinct instance of WaitForEventOnHelper - * -- binding different instance data -- for each of the pumps. Then, when a - * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine - * that LLSD with its discriminator to feed the future object. - */ - template <typename LISTENER> - class WaitForEventOnHelper - { - public: - WaitForEventOnHelper(const LISTENER& listener, int discriminator): - mListener(listener), - mDiscrim(discriminator) - {} - // this signature is required for an LLEventPump listener - bool operator()(const LLSD& event) - { - // our future object is defined to accept LLEventWithID - mListener(LLEventWithID(event, mDiscrim)); - // don't swallow the event, let other listeners see it - return false; - } - private: - LISTENER mListener; - const int mDiscrim; - }; - - /// WaitForEventOnHelper type-inference helper - template <typename LISTENER> - WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator) - { - return WaitForEventOnHelper<LISTENER>(listener, discriminator); - } -} // namespace LLEventDetail /** * This function waits for a reply on either of two specified LLEventPumps. @@ -400,6 +335,8 @@ waitForEventOn(SELF& self, */ LLSD errorException(const LLEventWithID& result, const std::string& desc); +} // namespace llcoro + /** * Exception thrown by errorException(). We don't call this LLEventError * because it's not an error in event processing: rather, this exception @@ -420,12 +357,17 @@ private: LLSD mData; }; +namespace llcoro +{ + /** * Like errorException(), save that this trips a fatal error using LL_ERRS * rather than throwing an exception. */ LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc); +} // namespace llcoro + /** * Certain event APIs require the name of an LLEventPump on which they should * post results. While it works to invent a distinct name and let @@ -466,14 +408,14 @@ public: template <typename SELF> LLSD wait(SELF& self) { - return waitForEventOn(self, mPump); + return llcoro::waitForEventOn(mPump); } template <typename SELF> LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, const LLSD& replyPumpNamePath=LLSD()) { - return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath); + return llcoro::postAndWait(event, requestPump, mPump, replyPumpNamePath); } private: @@ -513,21 +455,21 @@ public: template <typename SELF> LLEventWithID wait(SELF& self) { - return waitForEventOn(self, mPump0, mPump1); + return llcoro::waitForEventOn(mPump0, mPump1); } /// errorException(wait(self)) template <typename SELF> LLSD waitWithException(SELF& self) { - return errorException(wait(self), std::string("Error event on ") + getName1()); + return llcoro::errorException(wait(), std::string("Error event on ") + getName1()); } /// errorLog(wait(self)) template <typename SELF> LLSD waitWithLog(SELF& self) { - return errorLog(wait(self), std::string("Error event on ") + getName1()); + return llcoro::errorLog(wait(), std::string("Error event on ") + getName1()); } template <typename SELF> @@ -536,8 +478,8 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return postAndWait2(self, event, requestPump, mPump0, mPump1, - replyPump0NamePath, replyPump1NamePath); + return llcoro::postAndWait2(event, requestPump, mPump0, mPump1, + replyPump0NamePath, replyPump1NamePath); } template <typename SELF> @@ -546,9 +488,9 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorException(postAndWait(self, event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return llcoro::errorException(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } template <typename SELF> @@ -557,9 +499,9 @@ public: const LLSD& replyPump0NamePath=LLSD(), const LLSD& replyPump1NamePath=LLSD()) { - return errorLog(postAndWait(self, event, requestPump, - replyPump0NamePath, replyPump1NamePath), - std::string("Error event on ") + getName1()); + return llcoro::errorLog(postAndWait(event, requestPump, + replyPump0NamePath, replyPump1NamePath), + std::string("Error event on ") + getName1()); } private: diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 2c131de4e0..c3b0fec3c4 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -87,6 +87,8 @@ #include "lleventcoro.h" #include "../test/debug.h" +using namespace llcoro; + /***************************************************************************** * from the banana.cpp example program borrowed for test<1>() *****************************************************************************/ @@ -522,7 +524,7 @@ namespace tut // Construct the coroutine instance that will run explicit_wait. // Pass the ctor a callable that accepts the coroutine_type::self // param passed by the library. - coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1)); + boost::dcoroutines::coroutine<void()> coro(explicit_wait); // Start the coroutine coro(std::nothrow); // When the coroutine waits for the event pump, it returns here. |