summaryrefslogtreecommitdiff
path: root/indra/llcommon
diff options
context:
space:
mode:
Diffstat (limited to 'indra/llcommon')
-rwxr-xr-xindra/llcommon/llcoros.cpp86
-rwxr-xr-xindra/llcommon/llcoros.h67
-rwxr-xr-xindra/llcommon/lleventcoro.cpp113
-rwxr-xr-xindra/llcommon/lleventcoro.h176
-rwxr-xr-xindra/llcommon/tests/lleventcoro_test.cpp712
5 files changed, 563 insertions, 591 deletions
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp
index 2d0c419ae0..baaddcaed1 100755
--- a/indra/llcommon/llcoros.cpp
+++ b/indra/llcommon/llcoros.cpp
@@ -34,65 +34,11 @@
// std headers
// external library headers
#include <boost/bind.hpp>
-#include <boost/thread/tss.hpp>
// other Linden headers
#include "llevents.h"
#include "llerror.h"
#include "stringize.h"
-namespace {
-
-// do nothing, when we need nothing done
-void no_cleanup(LLCoros::coro::self*) {}
-
-// 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, because
-// at the leaf level we need it for context-switching. But since coroutines
-// are based on cooperative switching, we can cause the top-level entry point
-// to stash a static pointer to the currently-running coroutine, and manage it
-// appropriately as we switch out and back in. That eliminates the need to
-// pass it as an explicit parameter down through every level, which is
-// unfortunately viral in nature. Finding it implicitly rather than explicitly
-// allows minor maintenance in which a leaf-level function adds a new async
-// I/O call that suspends the calling coroutine, WITHOUT having to propagate
-// coro::self& through every function signature down to that point -- and of
-// course through every other caller of every such function.
-// We use a boost::thread_specific_ptr because each thread potentially has its
-// own distinct pool of coroutines.
-// This thread_specific_ptr does NOT own the 'self' object! It merely
-// identifies it. For this reason we instantiate it with a no-op cleanup
-// function.
-static boost::thread_specific_ptr<LLCoros::coro::self>
-sCurrentSelf(no_cleanup);
-
-} // anonymous
-
-//static
-LLCoros::coro::self& LLCoros::get_self()
-{
- coro::self* current_self = sCurrentSelf.get();
- if (! current_self)
- {
- LL_ERRS("LLCoros") << "Calling get_self() from non-coroutine context!" << LL_ENDL;
- }
- return *current_self;
-}
-
-LLCoros::Suspending::Suspending():
- mSuspended(sCurrentSelf.get())
-{
- // For the duration of our time away from this coroutine, sCurrentSelf
- // must NOT refer to this coroutine.
- sCurrentSelf.reset();
-}
-
-LLCoros::Suspending::~Suspending()
-{
- // Okay, we're back, reinstate previous value of sCurrentSelf.
- sCurrentSelf.reset(mSuspended);
-}
-
LLCoros::LLCoros():
// MAINT-2724: default coroutine stack size too small on Windows.
// Previously we used
@@ -114,7 +60,7 @@ bool LLCoros::cleanup(const LLSD&)
// since last tick?
if (mi->second->exited())
{
- LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
+ LL_INFOS("LLCoros") << "LLCoros: cleaning up coroutine " << mi->first << LL_ENDL;
// The erase() call will invalidate its passed iterator value --
// so increment mi FIRST -- but pass its original value to
// erase(). This is what postincrement is all about.
@@ -148,7 +94,7 @@ std::string LLCoros::generateDistinctName(const std::string& prefix) const
{
if (mCoros.find(name) == mCoros.end())
{
- LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
+ LL_INFOS("LLCoros") << "LLCoros: launching coroutine " << name << LL_ENDL;
return name;
}
}
@@ -168,10 +114,10 @@ bool LLCoros::kill(const std::string& name)
return true;
}
-std::string LLCoros::getName() const
+std::string LLCoros::getNameByID(const void* self_id) const
{
- // Walk the existing coroutines, looking for the current one.
- void* self_id = get_self().get_id();
+ // Walk the existing coroutines, looking for one from which the 'self_id'
+ // passed to us comes.
for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)
{
namespace coro_private = boost::dcoroutines::detail;
@@ -190,24 +136,10 @@ void LLCoros::setStackSize(S32 stacksize)
mStackSize = stacksize;
}
-namespace {
-
-// Top-level wrapper around caller's coroutine callable. This function accepts
-// the coroutine library's implicit coro::self& parameter and sets sCurrentSelf
-// but does not pass it down to the caller's callable.
-void toplevel(LLCoros::coro::self& self, const LLCoros::callable_t& callable)
-{
- sCurrentSelf.reset(&self);
- callable();
- sCurrentSelf.reset();
-}
-
-} // anonymous
-
/*****************************************************************************
* MUST BE LAST
*****************************************************************************/
-// Turn off MSVC optimizations for just LLCoros::launch() -- see
+// Turn off MSVC optimizations for just LLCoros::launchImpl() -- see
// DEV-32777. But MSVC doesn't support push/pop for optimization flags as it
// does for warning suppression, and we really don't want to force
// optimization ON for other code even in Debug or RelWithDebInfo builds.
@@ -218,13 +150,9 @@ void toplevel(LLCoros::coro::self& self, const LLCoros::callable_t& callable)
#pragma optimize("", off)
#endif // LL_MSVC
-std::string LLCoros::launch(const std::string& prefix, const callable_t& callable)
+std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro)
{
std::string name(generateDistinctName(prefix));
- // Wrap the caller's callable in our toplevel() function so we can manage
- // sCurrentSelf appropriately at startup and shutdown of each coroutine.
- coro* newCoro = new coro(boost::bind(toplevel, _1, callable), mStackSize);
- // Store it in our pointer map
mCoros.insert(name, newCoro);
/* Run the coroutine until its first wait, then return here */
(*newCoro)(std::nothrow);
diff --git a/indra/llcommon/llcoros.h b/indra/llcommon/llcoros.h
index 3f58a17aa9..01ee11da1a 100755
--- a/indra/llcommon/llcoros.h
+++ b/indra/llcommon/llcoros.h
@@ -32,8 +32,10 @@
#include <boost/dcoroutine/coroutine.hpp>
#include "llsingleton.h"
#include <boost/ptr_container/ptr_map.hpp>
-#include <boost/function.hpp>
#include <string>
+#include <boost/preprocessor/repetition/enum_params.hpp>
+#include <boost/preprocessor/repetition/enum_binary_params.hpp>
+#include <boost/preprocessor/iteration/local.hpp>
#include <stdexcept>
/**
@@ -78,8 +80,8 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
public:
/// Canonical boost::dcoroutines::coroutine signature we use
typedef boost::dcoroutines::coroutine<void()> coro;
- /// Canonical callable type
- typedef boost::function<void()> callable_t;
+ /// Canonical 'self' type
+ typedef coro::self self;
/**
* Create and start running a new coroutine with specified name. The name
@@ -92,33 +94,39 @@ public:
* {
* public:
* ...
- * // Do NOT NOT NOT accept reference params!
+ * // Do NOT NOT NOT accept reference params other than 'self'!
* // Pass by value only!
- * void myCoroutineMethod(std::string, LLSD);
+ * void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
* ...
* };
* ...
* std::string name = LLCoros::instance().launch(
- * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this,
+ * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
* "somestring", LLSD(17));
* @endcode
*
- * Your function/method can accept any parameters you want -- but ONLY BY
- * VALUE! Reference parameters are a BAD IDEA! You Have Been Warned. See
+ * Your function/method must accept LLCoros::self& as its first parameter.
+ * It can accept any other parameters you want -- but ONLY BY VALUE!
+ * Other reference parameters are a BAD IDEA! You Have Been Warned. See
* DEV-32777 comments for an explanation.
*
- * Pass a nullary callable. It works to directly pass a nullary free
- * function (or static method); for all other cases use boost::bind(). Of
- * course, for a non-static class method, the first parameter must be the
- * class instance. Any other parameters should be passed via the bind()
- * expression.
+ * Pass a callable that accepts the single LLCoros::self& parameter. It
+ * may work to pass a free function whose only parameter is 'self'; for
+ * all other cases use boost::bind(). Of course, for a non-static class
+ * method, the first parameter must be the class instance. Use the
+ * placeholder _1 for the 'self' parameter. Any other parameters should be
+ * passed via the bind() expression.
*
* launch() tweaks the suggested name so it won't collide with any
* existing coroutine instance, creates the coroutine instance, registers
* it with the tweaked name and runs it until its first wait. At that
* point it returns the tweaked name.
*/
- std::string launch(const std::string& prefix, const callable_t& callable);
+ template <typename CALLABLE>
+ std::string launch(const std::string& prefix, const CALLABLE& callable)
+ {
+ return launchImpl(prefix, new coro(callable, mStackSize));
+ }
/**
* Abort a running coroutine by name. Normally, when a coroutine either
@@ -130,34 +138,27 @@ public:
bool kill(const std::string& name);
/**
- * From within a coroutine, look up the (tweaked) name string by which
- * this coroutine is registered. Returns the empty string if not found
- * (e.g. if the coroutine was launched by hand rather than using
- * LLCoros::launch()).
+ * From within a coroutine, pass its @c self object to look up the
+ * (tweaked) name string by which this coroutine is registered. Returns
+ * the empty string if not found (e.g. if the coroutine was launched by
+ * hand rather than using LLCoros::launch()).
*/
- std::string getName() const;
-
- /// get the current coro::self& for those who really really care
- static 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
+ template <typename COROUTINE_SELF>
+ std::string getName(const COROUTINE_SELF& self) const
{
- public:
- Suspending();
- ~Suspending();
+ return getNameByID(self.get_id());
+ }
- private:
- coro::self* mSuspended;
- };
+ /// getName() by self.get_id()
+ std::string getNameByID(const void* self_id) const;
/// for delayed initialization
void setStackSize(S32 stacksize);
private:
- LLCoros();
friend class LLSingleton<LLCoros>;
+ LLCoros();
+ std::string launchImpl(const std::string& prefix, coro* newCoro);
std::string generateDistinctName(const std::string& prefix) const;
bool cleanup(const LLSD&);
diff --git a/indra/llcommon/lleventcoro.cpp b/indra/llcommon/lleventcoro.cpp
index be93e9c83b..81cc33fbba 100755
--- a/indra/llcommon/lleventcoro.cpp
+++ b/indra/llcommon/lleventcoro.cpp
@@ -34,24 +34,33 @@
#include <map>
// std headers
// external library headers
-#include <boost/dcoroutine/coroutine.hpp>
-#include <boost/dcoroutine/future.hpp>
// other Linden headers
#include "llsdserialize.h"
#include "llerror.h"
#include "llcoros.h"
-std::string LLEventDetail::listenerNameForCoro()
+std::string LLEventDetail::listenerNameForCoroImpl(const void* self_id)
{
- // If this coroutine was launched by LLCoros::launch(), find that name.
- std::string name(LLCoros::instance().getName());
+ // First, if this coroutine was launched by LLCoros::launch(), find that name.
+ std::string name(LLCoros::instance().getNameByID(self_id));
if (! name.empty())
{
return name;
}
+ // Apparently this coroutine wasn't launched by LLCoros::launch(). Check
+ // whether we have a memo for this self_id.
+ typedef std::map<const void*, std::string> MapType;
+ static MapType memo;
+ MapType::const_iterator found = memo.find(self_id);
+ if (found != memo.end())
+ {
+ // this coroutine instance has called us before, reuse same name
+ return found->second;
+ }
// this is the first time we've been called for this coroutine instance
name = LLEventPump::inventName("coro");
- LL_INFOS("LLEventCoro") << "listenerNameForCoro(): inventing coro name '"
+ memo[self_id] = name;
+ LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '"
<< name << "'" << LL_ENDL;
return name;
}
@@ -109,98 +118,6 @@ void LLEventDetail::storeToLLSDPath(LLSD& dest, const LLSD& rawPath, const LLSD&
*pdest = value;
}
-LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
- const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath)
-{
- // declare the future
- boost::dcoroutines::future<LLSD> 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(LLEventDetail::listenerNameForCoro());
- LLTempBoundListener connection(
- replyPump.getPump().listen(listenerName,
- voidlistener(boost::dcoroutines::make_callback(future))));
- // skip the "post" part if requestPump is default-constructed
- if (requestPump)
- {
- // If replyPumpNamePath is non-empty, store the replyPump name in the
- // request event.
- LLSD modevent(event);
- LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
- LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
- << " posting to " << requestPump.getPump().getName()
- << LL_ENDL;
-
- // *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
- // << ": " << modevent << LL_ENDL;
- requestPump.getPump().post(modevent);
- }
- LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
- << " about to wait on LLEventPump " << replyPump.getPump().getName()
- << LL_ENDL;
- // trying to dereference ("resolve") the future makes us wait for it
- LLSD value;
- {
- // instantiate Suspending to manage the "current" coroutine
- LLCoros::Suspending suspended;
- value = *future;
- } // destroy Suspending as soon as we're back
- LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
- << " resuming with " << value << LL_ENDL;
- // returning should disconnect the connection
- return value;
-}
-
-LLEventWithID postAndWait2(const LLSD& event,
- const LLEventPumpOrPumpName& requestPump,
- const LLEventPumpOrPumpName& replyPump0,
- const LLEventPumpOrPumpName& replyPump1,
- const LLSD& replyPump0NamePath,
- const LLSD& replyPump1NamePath)
-{
- // declare the future
- boost::dcoroutines::future<LLEventWithID> future(LLCoros::get_self());
- // either callback will assign a value to this future; listen on
- // each specified LLEventPump with a callback
- std::string name(LLEventDetail::listenerNameForCoro());
- LLTempBoundListener connection0(
- replyPump0.getPump().listen(name + "a",
- LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0)));
- LLTempBoundListener connection1(
- replyPump1.getPump().listen(name + "b",
- LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1)));
- // skip the "post" part if requestPump is default-constructed
- if (requestPump)
- {
- // If either replyPumpNamePath is non-empty, store the corresponding
- // replyPump name in the request event.
- LLSD modevent(event);
- LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
- replyPump0.getPump().getName());
- LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
- replyPump1.getPump().getName());
- LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
- << " posting to " << requestPump.getPump().getName()
- << ": " << modevent << LL_ENDL;
- requestPump.getPump().post(modevent);
- }
- LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
- << " 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;
- {
- // instantiate Suspending to manage "current" coroutine
- LLCoros::Suspending suspended;
- value = *future;
- } // destroy Suspending as soon as we're back
- LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
- << " resuming with (" << value.first << ", " << value.second << ")"
- << LL_ENDL;
- // returning should disconnect both connections
- return value;
-}
-
LLSD errorException(const LLEventWithID& result, const std::string& desc)
{
// If the result arrived on the error pump (pump 1), instead of
diff --git a/indra/llcommon/lleventcoro.h b/indra/llcommon/lleventcoro.h
index daf9360a2e..abbeeaa373 100755
--- a/indra/llcommon/lleventcoro.h
+++ b/indra/llcommon/lleventcoro.h
@@ -29,6 +29,8 @@
#if ! defined(LL_LLEVENTCORO_H)
#define LL_LLEVENTCORO_H
+#include <boost/dcoroutine/coroutine.hpp>
+#include <boost/dcoroutine/future.hpp>
#include <boost/optional.hpp>
#include <string>
#include <stdexcept>
@@ -100,6 +102,9 @@ LLVoidListener<LISTENER> voidlistener(const 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
@@ -111,9 +116,21 @@ namespace LLEventDetail
* 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 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
+ * value here, then be destroyed, only to be supplanted by a later
+ * coroutine (of the same or different type), and we'll end up
+ * "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.
*/
- std::string listenerNameForCoro();
+ template <typename COROUTINE_SELF>
+ std::string listenerNameForCoro(COROUTINE_SELF& self)
+ {
+ return listenerNameForCoroImpl(self.get_id());
+ }
/**
* Implement behavior described for postAndWait()'s @a replyPumpNamePath
@@ -142,7 +159,7 @@ namespace LLEventDetail
* convenience: the difference between this function and the sequence
* @code
* requestPump.post(myEvent);
- * LLSD reply = waitForEventOn(replyPump);
+ * LLSD reply = waitForEventOn(self, replyPump);
* @endcode
* is that the sequence above fails if the reply is posted immediately on
* @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the
@@ -184,16 +201,51 @@ namespace LLEventDetail
* @a replyPumpNamePath specifies the entry in the lowest-level structure in
* @a event into which to store <tt>replyPump.getName()</tt>.
*/
-LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
- const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD());
+template <typename SELF>
+LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
+ const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
+{
+ // declare the future
+ boost::dcoroutines::future<LLSD> future(self);
+ // make a callback that will assign a value to the future, and listen on
+ // the specified LLEventPump with that callback
+ std::string listenerName(LLEventDetail::listenerNameForCoro(self));
+ LLTempBoundListener connection(
+ replyPump.getPump().listen(listenerName,
+ voidlistener(boost::dcoroutines::make_callback(future))));
+ // skip the "post" part if requestPump is default-constructed
+ if (requestPump)
+ {
+ // If replyPumpNamePath is non-empty, store the replyPump name in the
+ // request event.
+ LLSD modevent(event);
+ LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
+ LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
+ << " posting to " << requestPump.getPump().getName()
+ << LL_ENDL;
+
+ // *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
+ // << ": " << modevent << LL_ENDL;
+ requestPump.getPump().post(modevent);
+ }
+ LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
+ << " about to wait on LLEventPump " << replyPump.getPump().getName()
+ << LL_ENDL;
+ // trying to dereference ("resolve") the future makes us wait for it
+ LLSD value(*future);
+ LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
+ << " resuming with " << value << LL_ENDL;
+ // returning should disconnect the connection
+ return value;
+}
/// Wait for the next event on the specified LLEventPump. Pass either the
/// LLEventPump& or its string name.
-inline
-LLSD waitForEventOn(const LLEventPumpOrPumpName& pump)
+template <typename SELF>
+LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump)
{
// This is now a convenience wrapper for postAndWait().
- return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump);
+ return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
}
/// return type for two-pump variant of waitForEventOn()
@@ -261,7 +313,7 @@ namespace LLEventDetail
* I'd have preferred to overload the name postAndWait() for both signatures.
* But consider the following ambiguous call:
* @code
- * postAndWait(LLSD(), requestPump, replyPump, "someString");
+ * postAndWait(self, LLSD(), requestPump, replyPump, "someString");
* @endcode
* "someString" could be converted to either LLSD (@a replyPumpNamePath for
* the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
@@ -270,29 +322,69 @@ namespace LLEventDetail
* It seems less burdensome to write postAndWait2() than to write either
* LLSD("someString") or LLEventOrPumpName("someString").
*/
-LLEventWithID postAndWait2(const LLSD& event,
+template <typename SELF>
+LLEventWithID postAndWait2(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLEventPumpOrPumpName& replyPump0,
const LLEventPumpOrPumpName& replyPump1,
const LLSD& replyPump0NamePath=LLSD(),
- const LLSD& replyPump1NamePath=LLSD());
+ const LLSD& replyPump1NamePath=LLSD())
+{
+ // declare the future
+ boost::dcoroutines::future<LLEventWithID> future(self);
+ // either callback will assign a value to this future; listen on
+ // each specified LLEventPump with a callback
+ std::string name(LLEventDetail::listenerNameForCoro(self));
+ LLTempBoundListener connection0(
+ replyPump0.getPump().listen(name + "a",
+ LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 0)));
+ LLTempBoundListener connection1(
+ replyPump1.getPump().listen(name + "b",
+ LLEventDetail::wfeoh(boost::dcoroutines::make_callback(future), 1)));
+ // skip the "post" part if requestPump is default-constructed
+ if (requestPump)
+ {
+ // If either replyPumpNamePath is non-empty, store the corresponding
+ // replyPump name in the request event.
+ LLSD modevent(event);
+ LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
+ replyPump0.getPump().getName());
+ LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
+ replyPump1.getPump().getName());
+ LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
+ << " posting to " << requestPump.getPump().getName()
+ << ": " << modevent << LL_ENDL;
+ requestPump.getPump().post(modevent);
+ }
+ LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
+ << " 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(*future);
+ LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
+ << " resuming with (" << value.first << ", " << value.second << ")"
+ << LL_ENDL;
+ // returning should disconnect both connections
+ return value;
+}
/**
* Wait for the next event on either of two specified LLEventPumps.
*/
-inline
+template <typename SELF>
LLEventWithID
-waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
+waitForEventOn(SELF& self,
+ const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
{
// This is now a convenience wrapper for postAndWait2().
- return postAndWait2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
+ return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
}
/**
* Helper for the two-pump variant of waitForEventOn(), e.g.:
*
* @code
- * LLSD reply = errorException(waitForEventOn(replyPump, errorPump),
+ * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
* "error response from login.cgi");
* @endcode
*
@@ -362,16 +454,26 @@ public:
/**
* Wait for an event on this LLEventPump.
+ *
+ * @note
+ * The other major usage pattern we considered was to bind @c self at
+ * LLCoroEventPump construction time, which would avoid passing the
+ * parameter to each wait() call. But if we were going to bind @c self as
+ * a class member, we'd need to specify a class template parameter
+ * indicating its type. The big advantage of passing it to the wait() call
+ * is that the type can be implicit.
*/
- LLSD wait()
+ template <typename SELF>
+ LLSD wait(SELF& self)
{
- return ::waitForEventOn(mPump);
+ return waitForEventOn(self, mPump);
}
- LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump,
+ template <typename SELF>
+ LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPumpNamePath=LLSD())
{
- return ::postAndWait(event, requestPump, mPump, replyPumpNamePath);
+ return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
}
private:
@@ -407,49 +509,55 @@ public:
/// request pump 1
LLEventPump& getPump1() { return mPump1; }
- /// waitForEventOn(either of our two LLEventPumps)
- LLEventWithID wait()
+ /// waitForEventOn(self, either of our two LLEventPumps)
+ template <typename SELF>
+ LLEventWithID wait(SELF& self)
{
- return waitForEventOn(mPump0, mPump1);
+ return waitForEventOn(self, mPump0, mPump1);
}
- /// errorException(wait())
- LLSD waitWithException()
+ /// errorException(wait(self))
+ template <typename SELF>
+ LLSD waitWithException(SELF& self)
{
- return errorException(wait(), std::string("Error event on ") + getName1());
+ return errorException(wait(self), std::string("Error event on ") + getName1());
}
- /// errorLog(wait())
- LLSD waitWithLog()
+ /// errorLog(wait(self))
+ template <typename SELF>
+ LLSD waitWithLog(SELF& self)
{
- return errorLog(wait(), std::string("Error event on ") + getName1());
+ return errorLog(wait(self), std::string("Error event on ") + getName1());
}
- LLEventWithID postAndWait(const LLSD& event,
+ template <typename SELF>
+ LLEventWithID postAndWait(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
- return postAndWait2(event, requestPump, mPump0, mPump1,
+ return postAndWait2(self, event, requestPump, mPump0, mPump1,
replyPump0NamePath, replyPump1NamePath);
}
- LLSD postAndWaitWithException(const LLSD& event,
+ template <typename SELF>
+ LLSD postAndWaitWithException(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
- return errorException(postAndWait(event, requestPump,
+ return errorException(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
- LLSD postAndWaitWithLog(const LLSD& event,
+ template <typename SELF>
+ LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
const LLEventPumpOrPumpName& requestPump,
const LLSD& replyPump0NamePath=LLSD(),
const LLSD& replyPump1NamePath=LLSD())
{
- return errorLog(postAndWait(event, requestPump,
+ return errorLog(postAndWait(self, event, requestPump,
replyPump0NamePath, replyPump1NamePath),
std::string("Error event on ") + getName1());
}
diff --git a/indra/llcommon/tests/lleventcoro_test.cpp b/indra/llcommon/tests/lleventcoro_test.cpp
index da927038ab..2096807e53 100755
--- a/indra/llcommon/tests/lleventcoro_test.cpp
+++ b/indra/llcommon/tests/lleventcoro_test.cpp
@@ -82,7 +82,6 @@
#include "llevents.h"
#include "tests/wrapllerrs.h"
#include "stringize.h"
-#include "llcoros.h"
#include "lleventcoro.h"
#include "../test/debug.h"
@@ -122,6 +121,9 @@ typedef coroutine<std::string::iterator(void)> match_coroutine_type;
/*****************************************************************************
* Test helpers
*****************************************************************************/
+// I suspect this will be typical of coroutines used in Linden software
+typedef boost::dcoroutines::coroutine<void()> coroutine_type;
+
/// Simulate an event API whose response is immediate: sent on receipt of the
/// initial request, rather than after some delay. This is the case that
/// distinguishes postAndWait() from calling post(), then calling
@@ -160,7 +162,306 @@ private:
*****************************************************************************/
namespace tut
{
- struct coroutine_data {};
+ struct coroutine_data
+ {
+ // Define coroutine bodies as methods here so they can use ensure*()
+
+ void explicit_wait(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ // ... do whatever preliminary stuff must happen ...
+
+ // declare the future
+ boost::dcoroutines::future<LLSD> future(self);
+ // tell the future what to wait for
+ LLTempBoundListener connection(
+ LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future))));
+ ensure("Not yet", ! future);
+ // attempting to dereference ("resolve") the future causes the calling
+ // coroutine to wait for it
+ debug("about to wait");
+ result = *future;
+ ensure("Got it", future);
+ }
+ END
+ }
+
+ void waitForEventOn1(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ result = waitForEventOn(self, "source");
+ }
+ END
+ }
+
+ void waitForEventOn2(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLEventWithID pair = waitForEventOn(self, "reply", "error");
+ result = pair.first;
+ which = pair.second;
+ debug(STRINGIZE("result = " << result << ", which = " << which));
+ }
+ END
+ }
+
+ void postAndWait1(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ result = postAndWait(self,
+ LLSDMap("value", 17), // request event
+ immediateAPI.getPump(), // requestPump
+ "reply1", // replyPump
+ "reply"); // request["reply"] = name
+ }
+ END
+ }
+
+ void postAndWait2(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLEventWithID pair = ::postAndWait2(self,
+ LLSDMap("value", 18),
+ immediateAPI.getPump(),
+ "reply2",
+ "error2",
+ "reply",
+ "error");
+ result = pair.first;
+ which = pair.second;
+ debug(STRINGIZE("result = " << result << ", which = " << which));
+ }
+ END
+ }
+
+ void postAndWait2_1(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLEventWithID pair = ::postAndWait2(self,
+ LLSDMap("value", 18)("fail", LLSD()),
+ immediateAPI.getPump(),
+ "reply2",
+ "error2",
+ "reply",
+ "error");
+ result = pair.first;
+ which = pair.second;
+ debug(STRINGIZE("result = " << result << ", which = " << which));
+ }
+ END
+ }
+
+ void coroPump(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPump waiter;
+ replyName = waiter.getName();
+ result = waiter.wait(self);
+ }
+ END
+ }
+
+ void coroPumpPost(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPump waiter;
+ result = waiter.postAndWait(self, LLSDMap("value", 17),
+ immediateAPI.getPump(), "reply");
+ }
+ END
+ }
+
+ void coroPumps(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ LLEventWithID pair(waiter.wait(self));
+ result = pair.first;
+ which = pair.second;
+ }
+ END
+ }
+
+ void coroPumpsNoEx(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ result = waiter.waitWithException(self);
+ }
+ END
+ }
+
+ void coroPumpsEx(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ try
+ {
+ result = waiter.waitWithException(self);
+ debug("no exception");
+ }
+ catch (const LLErrorEvent& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ errordata = e.getData();
+ }
+ }
+ END
+ }
+
+ void coroPumpsNoLog(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ result = waiter.waitWithLog(self);
+ }
+ END
+ }
+
+ void coroPumpsLog(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ replyName = waiter.getName0();
+ errorName = waiter.getName1();
+ WrapLLErrs capture;
+ try
+ {
+ result = waiter.waitWithLog(self);
+ debug("no exception");
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ threw = e.what();
+ }
+ }
+ END
+ }
+
+ void coroPumpsPost(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ LLEventWithID pair(waiter.postAndWait(self, LLSDMap("value", 23),
+ immediateAPI.getPump(), "reply", "error"));
+ result = pair.first;
+ which = pair.second;
+ }
+ END
+ }
+
+ void coroPumpsPost_1(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ LLEventWithID pair(
+ waiter.postAndWait(self, LLSDMap("value", 23)("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error"));
+ result = pair.first;
+ which = pair.second;
+ }
+ END
+ }
+
+ void coroPumpsPostNoEx(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ result = waiter.postAndWaitWithException(self, LLSDMap("value", 8),
+ immediateAPI.getPump(), "reply", "error");
+ }
+ END
+ }
+
+ void coroPumpsPostEx(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ try
+ {
+ result = waiter.postAndWaitWithException(self,
+ LLSDMap("value", 9)("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error");
+ debug("no exception");
+ }
+ catch (const LLErrorEvent& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ errordata = e.getData();
+ }
+ }
+ END
+ }
+
+ void coroPumpsPostNoLog(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ result = waiter.postAndWaitWithLog(self, LLSDMap("value", 30),
+ immediateAPI.getPump(), "reply", "error");
+ }
+ END
+ }
+
+ void coroPumpsPostLog(coroutine_type::self& self)
+ {
+ BEGIN
+ {
+ LLCoroEventPumps waiter;
+ WrapLLErrs capture;
+ try
+ {
+ result = waiter.postAndWaitWithLog(self,
+ LLSDMap("value", 31)("fail", LLSD()),
+ immediateAPI.getPump(), "reply", "error");
+ debug("no exception");
+ }
+ catch (const WrapLLErrs::FatalException& e)
+ {
+ debug(STRINGIZE("exception " << e.what()));
+ threw = e.what();
+ }
+ }
+ END
+ }
+
+ void ensure_done(coroutine_type& coro)
+ {
+ ensure("coroutine complete", ! coro);
+ }
+
+ ImmediateAPI immediateAPI;
+ std::string replyName, errorName, threw;
+ LLSD result, errordata;
+ int which;
+ };
typedef test_group<coroutine_data> coroutine_group;
typedef coroutine_group::object object;
coroutine_group coroutinegrp("coroutine");
@@ -210,57 +511,16 @@ namespace tut
ensure("done", ! matcher);
}
- // use static data so we can intersperse coroutine functions with the
- // tests that engage them
- ImmediateAPI immediateAPI;
- std::string replyName, errorName, threw;
- LLSD result, errordata;
- int which;
-
- // reinit vars at the start of each test
- void clear()
- {
- replyName.clear();
- errorName.clear();
- threw.clear();
- result = LLSD();
- errordata = LLSD();
- which = 0;
- }
-
- void explicit_wait(boost::dcoroutines::coroutine<void()>::self& self)
- {
- BEGIN
- {
- // ... do whatever preliminary stuff must happen ...
-
- // declare the future
- boost::dcoroutines::future<LLSD> future(self);
- // tell the future what to wait for
- LLTempBoundListener connection(
- LLEventPumps::instance().obtain("source").listen("coro", voidlistener(boost::dcoroutines::make_callback(future))));
- ensure("Not yet", ! future);
- // attempting to dereference ("resolve") the future causes the calling
- // coroutine to wait for it
- debug("about to wait");
- result = *future;
- ensure("Got it", future);
- }
- END
- }
-
template<> template<>
void object::test<2>()
{
- clear();
set_test_name("explicit_wait");
DEBUG;
// 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.
- boost::dcoroutines::coroutine<void()>
- coro(explicit_wait);
+ coroutine_type coro(boost::bind(&coroutine_data::explicit_wait, this, _1));
// Start the coroutine
coro(std::nothrow);
// When the coroutine waits for the event pump, it returns here.
@@ -268,56 +528,37 @@ namespace tut
// Satisfy the wait.
LLEventPumps::instance().obtain("source").post("received");
// Now wait for the coroutine to complete.
- ensure("coroutine complete", ! coro);
+ ensure_done(coro);
// ensure the coroutine ran and woke up again with the intended result
ensure_equals(result.asString(), "received");
}
- void waitForEventOn1()
- {
- BEGIN
- {
- result = waitForEventOn("source");
- }
- END
- }
-
template<> template<>
void object::test<3>()
{
- clear();
set_test_name("waitForEventOn1");
DEBUG;
- LLCoros::instance().launch("test<3>", waitForEventOn1);
+ coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain("source").post("received");
debug("back from send");
+ ensure_done(coro);
ensure_equals(result.asString(), "received");
}
- void waitForEventOn2()
- {
- BEGIN
- {
- LLEventWithID pair = waitForEventOn("reply", "error");
- result = pair.first;
- which = pair.second;
- debug(STRINGIZE("result = " << result << ", which = " << which));
- }
- END
- }
-
template<> template<>
void object::test<4>()
{
- clear();
set_test_name("waitForEventOn2 reply");
{
DEBUG;
- LLCoros::instance().launch("test<4>", waitForEventOn2);
+ coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain("reply").post("received");
debug("back from send");
+ ensure_done(coro);
}
ensure_equals(result.asString(), "received");
ensure_equals("which pump", which, 0);
@@ -326,65 +567,43 @@ namespace tut
template<> template<>
void object::test<5>()
{
- clear();
set_test_name("waitForEventOn2 error");
DEBUG;
- LLCoros::instance().launch("test<5>", waitForEventOn2);
+ coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain("error").post("badness");
debug("back from send");
+ ensure_done(coro);
ensure_equals(result.asString(), "badness");
ensure_equals("which pump", which, 1);
}
- void coroPump()
- {
- BEGIN
- {
- LLCoroEventPump waiter;
- replyName = waiter.getName();
- result = waiter.wait();
- }
- END
- }
-
template<> template<>
void object::test<6>()
{
- clear();
set_test_name("coroPump");
DEBUG;
- LLCoros::instance().launch("test<6>", coroPump);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
debug("back from send");
+ ensure_done(coro);
ensure_equals(result.asString(), "received");
}
- void coroPumps()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- LLEventWithID pair(waiter.wait());
- result = pair.first;
- which = pair.second;
- }
- END
- }
-
template<> template<>
void object::test<7>()
{
- clear();
set_test_name("coroPumps reply");
DEBUG;
- LLCoros::instance().launch("test<7>", coroPumps);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
debug("back from send");
+ ensure_done(coro);
ensure_equals(result.asString(), "received");
ensure_equals("which pump", which, 0);
}
@@ -392,389 +611,188 @@ namespace tut
template<> template<>
void object::test<8>()
{
- clear();
set_test_name("coroPumps error");
DEBUG;
- LLCoros::instance().launch("test<8>", coroPumps);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain(errorName).post("badness");
debug("back from send");
+ ensure_done(coro);
ensure_equals(result.asString(), "badness");
ensure_equals("which pump", which, 1);
}
- void coroPumpsNoEx()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- result = waiter.waitWithException();
- }
- END
- }
-
template<> template<>
void object::test<9>()
{
- clear();
set_test_name("coroPumpsNoEx");
DEBUG;
- LLCoros::instance().launch("test<9>", coroPumpsNoEx);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
debug("back from send");
+ ensure_done(coro);
ensure_equals(result.asString(), "received");
}
- void coroPumpsEx()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- try
- {
- result = waiter.waitWithException();
- debug("no exception");
- }
- catch (const LLErrorEvent& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- errordata = e.getData();
- }
- }
- END
- }
-
template<> template<>
void object::test<10>()
{
- clear();
set_test_name("coroPumpsEx");
DEBUG;
- LLCoros::instance().launch("test<10>", coroPumpsEx);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain(errorName).post("badness");
debug("back from send");
+ ensure_done(coro);
ensure("no result", result.isUndefined());
ensure_equals("got error", errordata.asString(), "badness");
}
- void coroPumpsNoLog()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- result = waiter.waitWithLog();
- }
- END
- }
-
template<> template<>
void object::test<11>()
{
- clear();
set_test_name("coroPumpsNoLog");
DEBUG;
- LLCoros::instance().launch("test<11>", coroPumpsNoLog);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain(replyName).post("received");
debug("back from send");
+ ensure_done(coro);
ensure_equals(result.asString(), "received");
}
- void coroPumpsLog()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- replyName = waiter.getName0();
- errorName = waiter.getName1();
- WrapLLErrs capture;
- try
- {
- result = waiter.waitWithLog();
- debug("no exception");
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- threw = e.what();
- }
- }
- END
- }
-
template<> template<>
void object::test<12>()
{
- clear();
set_test_name("coroPumpsLog");
DEBUG;
- LLCoros::instance().launch("test<12>", coroPumpsLog);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1));
+ coro(std::nothrow);
debug("about to send");
LLEventPumps::instance().obtain(errorName).post("badness");
debug("back from send");
+ ensure_done(coro);
ensure("no result", result.isUndefined());
ensure_contains("got error", threw, "badness");
}
- void postAndWait1()
- {
- BEGIN
- {
- result = postAndWait(LLSDMap("value", 17), // request event
- immediateAPI.getPump(), // requestPump
- "reply1", // replyPump
- "reply"); // request["reply"] = name
- }
- END
- }
-
template<> template<>
void object::test<13>()
{
- clear();
set_test_name("postAndWait1");
DEBUG;
- LLCoros::instance().launch("test<13>", postAndWait1);
+ coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure_equals(result.asInteger(), 18);
}
- void postAndWait2()
- {
- BEGIN
- {
- LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18),
- immediateAPI.getPump(),
- "reply2",
- "error2",
- "reply",
- "error");
- result = pair.first;
- which = pair.second;
- debug(STRINGIZE("result = " << result << ", which = " << which));
- }
- END
- }
-
template<> template<>
void object::test<14>()
{
- clear();
set_test_name("postAndWait2");
DEBUG;
- LLCoros::instance().launch("test<14>", postAndWait2);
+ coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure_equals(result.asInteger(), 19);
ensure_equals(which, 0);
}
- void postAndWait2_1()
- {
- BEGIN
- {
- LLEventWithID pair = ::postAndWait2(LLSDMap("value", 18)("fail", LLSD()),
- immediateAPI.getPump(),
- "reply2",
- "error2",
- "reply",
- "error");
- result = pair.first;
- which = pair.second;
- debug(STRINGIZE("result = " << result << ", which = " << which));
- }
- END
- }
-
template<> template<>
void object::test<15>()
{
- clear();
set_test_name("postAndWait2_1");
DEBUG;
- LLCoros::instance().launch("test<15>", postAndWait2_1);
+ coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure_equals(result.asInteger(), 19);
ensure_equals(which, 1);
}
- void coroPumpPost()
- {
- BEGIN
- {
- LLCoroEventPump waiter;
- result = waiter.postAndWait(LLSDMap("value", 17),
- immediateAPI.getPump(), "reply");
- }
- END
- }
-
template<> template<>
void object::test<16>()
{
- clear();
set_test_name("coroPumpPost");
DEBUG;
- LLCoros::instance().launch("test<16>", coroPumpPost);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure_equals(result.asInteger(), 18);
}
- void coroPumpsPost()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- LLEventWithID pair(waiter.postAndWait(LLSDMap("value", 23),
- immediateAPI.getPump(), "reply", "error"));
- result = pair.first;
- which = pair.second;
- }
- END
- }
-
template<> template<>
void object::test<17>()
{
- clear();
set_test_name("coroPumpsPost reply");
DEBUG;
- LLCoros::instance().launch("test<17>", coroPumpsPost);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure_equals(result.asInteger(), 24);
ensure_equals("which pump", which, 0);
}
- void coroPumpsPost_1()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- LLEventWithID pair(
- waiter.postAndWait(LLSDMap("value", 23)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error"));
- result = pair.first;
- which = pair.second;
- }
- END
- }
-
template<> template<>
void object::test<18>()
{
- clear();
set_test_name("coroPumpsPost error");
DEBUG;
- LLCoros::instance().launch("test<18>", coroPumpsPost_1);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure_equals(result.asInteger(), 24);
ensure_equals("which pump", which, 1);
}
- void coroPumpsPostNoEx()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- result = waiter.postAndWaitWithException(LLSDMap("value", 8),
- immediateAPI.getPump(), "reply", "error");
- }
- END
- }
-
template<> template<>
void object::test<19>()
{
- clear();
set_test_name("coroPumpsPostNoEx");
DEBUG;
- LLCoros::instance().launch("test<19>", coroPumpsPostNoEx);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure_equals(result.asInteger(), 9);
}
- void coroPumpsPostEx()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- try
- {
- result = waiter.postAndWaitWithException(
- LLSDMap("value", 9)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error");
- debug("no exception");
- }
- catch (const LLErrorEvent& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- errordata = e.getData();
- }
- }
- END
- }
-
template<> template<>
void object::test<20>()
{
- clear();
set_test_name("coroPumpsPostEx");
DEBUG;
- LLCoros::instance().launch("test<20>", coroPumpsPostEx);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure("no result", result.isUndefined());
ensure_equals("got error", errordata.asInteger(), 10);
}
- void coroPumpsPostNoLog()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- result = waiter.postAndWaitWithLog(LLSDMap("value", 30),
- immediateAPI.getPump(), "reply", "error");
- }
- END
- }
-
template<> template<>
void object::test<21>()
{
- clear();
set_test_name("coroPumpsPostNoLog");
DEBUG;
- LLCoros::instance().launch("test<21>", coroPumpsPostNoLog);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure_equals(result.asInteger(), 31);
}
- void coroPumpsPostLog()
- {
- BEGIN
- {
- LLCoroEventPumps waiter;
- WrapLLErrs capture;
- try
- {
- result = waiter.postAndWaitWithLog(
- LLSDMap("value", 31)("fail", LLSD()),
- immediateAPI.getPump(), "reply", "error");
- debug("no exception");
- }
- catch (const WrapLLErrs::FatalException& e)
- {
- debug(STRINGIZE("exception " << e.what()));
- threw = e.what();
- }
- }
- END
- }
-
template<> template<>
void object::test<22>()
{
- clear();
set_test_name("coroPumpsPostLog");
DEBUG;
- LLCoros::instance().launch("test<22>", coroPumpsPostLog);
+ coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1));
+ coro(std::nothrow);
+ ensure_done(coro);
ensure("no result", result.isUndefined());
ensure_contains("got error", threw, "32");
}