diff options
Diffstat (limited to 'indra/llcommon/lleventcoro.cpp')
-rwxr-xr-x | indra/llcommon/lleventcoro.cpp | 112 |
1 files changed, 110 insertions, 2 deletions
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 |