diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index 548a6d22be0ffbea867975a7935c453c218b58f4..d16bf0160b088e595cf8075bcee88b914d0a9fdc 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -51,14 +51,32 @@ boost::thread_specific_ptr<LLCoros::CoroData> LLCoros::sCurrentCoro(LLCoros::no_cleanup); //static -LLCoros::coro::self& LLCoros::get_self() +LLCoros::CoroData& LLCoros::get_CoroData(const std::string& caller) { CoroData* current = sCurrentCoro.get(); if (! current) { - LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL; + LL_ERRS("LLCoros") << "Calling " << caller << " from non-coroutine context!" << LL_ENDL; } - return *current->mSelf; + return *current; +} + +//static +LLCoros::coro::self& LLCoros::get_self() +{ + return *get_CoroData("get_self()").mSelf; +} + +//static +void LLCoros::set_consuming(bool consuming) +{ + get_CoroData("set_consuming()").mConsuming = consuming; +} + +//static +bool LLCoros::get_consuming() +{ + return get_CoroData("get_consuming()").mConsuming; } llcoro::Suspending::Suspending(): @@ -245,6 +263,8 @@ LLCoros::CoroData::CoroData(CoroData* prev, const std::string& name, // Wrap the caller's callable in our toplevel() function so we can manage // sCurrentCoro appropriately at startup and shutdown of each coroutine. mCoro(boost::bind(toplevel, _1, this, callable), stacksize), + // don't consume events unless specifically directed + mConsuming(false), mSelf(0) { } diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h index 56eed8cafed0a3717c7e16735cf1388f81349833..0b1f58f48ebcd06b737a192beffdc95b3a320349 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -150,6 +150,18 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros> /// get the current coro::self& for those who really really care static coro::self& get_self(); + /** + * Most coroutines, most of the time, don't "consume" the events for which + * they're suspending. This way, an arbitrary number of listeners (whether + * coroutines or simple callbacks) can be registered on a particular + * LLEventPump, every listener responding to each of the events on that + * LLEventPump. But a particular coroutine can assert that it will consume + * each event for which it suspends. (See also llcoro::postAndSuspend(), + * llcoro::VoidListener) + */ + static void set_consuming(bool consuming); + static bool get_consuming(); + private: LLCoros(); friend class LLSingleton<LLCoros>; @@ -159,6 +171,7 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros> struct CoroData; static void no_cleanup(CoroData*); static void toplevel(coro::self& self, CoroData* data, const callable_t& callable); + static CoroData& get_CoroData(const std::string& caller); S32 mStackSize; @@ -178,6 +191,8 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros> 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, diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp index 1c3fb4325d7e193c356a885aa056905d2e72c683..9b7e8fb65fdf126fc07652c6feef44c2782cc649 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -167,7 +167,7 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath) { // declare the future - boost::dcoroutines::future<LLSD> future(LLCoros::get_self()); + boost::dcoroutines::future<LLSD_consumed> future(LLCoros::get_self()); // make a callback that will assign a value to the future, and listen on // the specified LLEventPump with that callback std::string listenerName(listenerNameForCoro()); @@ -193,21 +193,25 @@ LLSD llcoro::postAndSuspend(const LLSD& event, const LLEventPumpOrPumpName& requ << " about to wait on LLEventPump " << replyPump.getPump().getName() << LL_ENDL; // trying to dereference ("resolve") the future makes us wait for it - LLSD value; + LLSD_consumed value; { // instantiate Suspending to manage the "current" coroutine llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << listenerName - << " resuming with " << value << LL_ENDL; + << " resuming with " << value.first << LL_ENDL; + // immediately set consumed according to consuming + *value.second = LLCoros::get_consuming(); // returning should disconnect the connection - return value; + return value.first; } namespace { +typedef std::pair<LLEventWithID, bool*> LLEventWithID_consumed; + /** * This helper is specifically for the two-pump version of suspendUntilEventOn(). * We use a single future object, but we want to listen on two pumps with it. @@ -227,13 +231,15 @@ class WaitForEventOnHelper 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; + bool consumed = false; + // our future object is defined to accept LLEventWithID_consumed + mListener(LLEventWithID_consumed(LLEventWithID(event, mDiscrim), &consumed)); + // tell LLEventPump whether or not event was consumed + return consumed; } private: LISTENER mListener; @@ -260,7 +266,7 @@ LLEventWithID postAndSuspend2(const LLSD& event, const LLSD& replyPump1NamePath) { // declare the future - boost::dcoroutines::future<LLEventWithID> future(LLCoros::get_self()); + boost::dcoroutines::future<LLEventWithID_consumed> future(LLCoros::get_self()); // either callback will assign a value to this future; listen on // each specified LLEventPump with a callback std::string name(listenerNameForCoro()); @@ -289,17 +295,19 @@ LLEventWithID postAndSuspend2(const LLSD& event, << " about to wait on LLEventPumps " << replyPump0.getPump().getName() << ", " << replyPump1.getPump().getName() << LL_ENDL; // trying to dereference ("resolve") the future makes us wait for it - LLEventWithID value; + LLEventWithID_consumed value; { // instantiate Suspending to manage "current" coroutine llcoro::Suspending suspended; value = *future; } // destroy Suspending as soon as we're back LL_DEBUGS("lleventcoro") << "postAndSuspend(): coroutine " << name - << " resuming with (" << value.first << ", " << value.second << ")" - << LL_ENDL; + << " resuming with (" << value.first.first + << ", " << value.first.second << ")" << LL_ENDL; + // tell LLEventPump whether we're consuming + *value.second = LLCoros::get_consuming(); // returning should disconnect both connections - return value; + return value.first; } LLSD errorException(const LLEventWithID& result, const std::string& desc) diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h index bcc8cdda1de97ff7b4ece784cf95c7b0c207b65b..acf2ad24a4063e2ebd74683f3eb9d759991471bf 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -32,6 +32,7 @@ #include <boost/optional.hpp> #include <string> #include <stdexcept> +#include <utility> // std::pair #include "llevents.h" #include "llerror.h" @@ -75,6 +76,8 @@ class LLEventPumpOrPumpName namespace llcoro { +typedef std::pair<LLSD, bool*> LLSD_consumed; + /// 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> @@ -84,11 +87,13 @@ class VoidListener VoidListener(const LISTENER& listener): mListener(listener) {} + bool operator()(const LLSD& event) { - mListener(event); - // don't swallow the event, let other listeners see it - return false; + bool consumed = false; + mListener(LLSD_consumed(event, &consumed)); + // tell upstream LLEventPump whether listener consumed + return consumed; } private: LISTENER mListener; diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp index 1ee79e9eb654cb6e3bf427d9f48933b54399994d..da1439418f601b31ca67c799090db18cdfdb84c4 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -240,7 +240,7 @@ namespace tut // ... do whatever preliminary stuff must happen ... // declare the future - boost::dcoroutines::future<LLSD> future(self); + boost::dcoroutines::future<llcoro::LLSD_consumed> future(self); // tell the future what to suspend for LLTempBoundListener connection( LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future)))); @@ -248,7 +248,7 @@ namespace tut // attempting to dereference ("resolve") the future causes the calling // coroutine to suspend for it debug("about to suspend"); - result = *future; + result = (*future).first; ensure("Got it", future); } END