summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rwxr-xr-xindra/llcommon/llcoros.cpp6
-rwxr-xr-xindra/llcommon/llcoros.h27
-rwxr-xr-xindra/llcommon/lleventcoro.cpp112
-rwxr-xr-xindra/llcommon/lleventcoro.h132
-rwxr-xr-xindra/llcommon/tests/lleventcoro_test.cpp4
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.