diff options
Diffstat (limited to 'indra/llcommon')
| -rwxr-xr-x | indra/llcommon/llcoros.cpp | 86 | ||||
| -rwxr-xr-x | indra/llcommon/llcoros.h | 67 | ||||
| -rwxr-xr-x | indra/llcommon/lleventcoro.cpp | 113 | ||||
| -rwxr-xr-x | indra/llcommon/lleventcoro.h | 176 | ||||
| -rwxr-xr-x | indra/llcommon/tests/lleventcoro_test.cpp | 712 | 
5 files changed, 591 insertions, 563 deletions
diff --git a/indra/llcommon/llcoros.cpp b/indra/llcommon/llcoros.cpp index baaddcaed1..2d0c419ae0 100755 --- a/indra/llcommon/llcoros.cpp +++ b/indra/llcommon/llcoros.cpp @@ -34,11 +34,65 @@  // 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 @@ -60,7 +114,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. @@ -94,7 +148,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;          }      } @@ -114,10 +168,10 @@ bool LLCoros::kill(const std::string& name)      return true;  } -std::string LLCoros::getNameByID(const void* self_id) const +std::string LLCoros::getName() const  { -    // Walk the existing coroutines, looking for one from which the 'self_id' -    // passed to us comes. +    // Walk the existing coroutines, looking for the current one. +    void* self_id = get_self().get_id();      for (CoroMap::const_iterator mi(mCoros.begin()), mend(mCoros.end()); mi != mend; ++mi)      {          namespace coro_private = boost::dcoroutines::detail; @@ -136,10 +190,24 @@ 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::launchImpl() -- see +// Turn off MSVC optimizations for just LLCoros::launch() -- 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. @@ -150,9 +218,13 @@ void LLCoros::setStackSize(S32 stacksize)  #pragma optimize("", off)  #endif // LL_MSVC -std::string LLCoros::launchImpl(const std::string& prefix, coro* newCoro) +std::string LLCoros::launch(const std::string& prefix, const callable_t& callable)  {      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 01ee11da1a..3f58a17aa9 100755 --- a/indra/llcommon/llcoros.h +++ b/indra/llcommon/llcoros.h @@ -32,10 +32,8 @@  #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>  /** @@ -80,8 +78,8 @@ class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>  public:      /// Canonical boost::dcoroutines::coroutine signature we use      typedef boost::dcoroutines::coroutine<void()> coro; -    /// Canonical 'self' type -    typedef coro::self self; +    /// Canonical callable type +    typedef boost::function<void()> callable_t;      /**       * Create and start running a new coroutine with specified name. The name @@ -94,39 +92,33 @@ public:       * {       * public:       *     ... -     *     // Do NOT NOT NOT accept reference params other than 'self'! +     *     // Do NOT NOT NOT accept reference params!       *     // Pass by value only! -     *     void myCoroutineMethod(LLCoros::self& self, std::string, LLSD); +     *     void myCoroutineMethod(std::string, LLSD);       *     ...       * };       * ...       * std::string name = LLCoros::instance().launch( -     *    "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1, +     *    "mycoro", boost::bind(&MyClass::myCoroutineMethod, this,       *                          "somestring", LLSD(17));       * @endcode       * -     * 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 +     * Your function/method can accept any parameters you want -- but ONLY BY +     * VALUE! Reference parameters are a BAD IDEA! You Have Been Warned. See       * DEV-32777 comments for an explanation.       * -     * 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. +     * 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.       *       * 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.       */ -    template <typename CALLABLE> -    std::string launch(const std::string& prefix, const CALLABLE& callable) -    { -        return launchImpl(prefix, new coro(callable, mStackSize)); -    } +    std::string launch(const std::string& prefix, const callable_t& callable);      /**       * Abort a running coroutine by name. Normally, when a coroutine either @@ -138,27 +130,34 @@ public:      bool kill(const std::string& name);      /** -     * 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()). +     * 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()).       */ -    template <typename COROUTINE_SELF> -    std::string getName(const COROUTINE_SELF& self) const +    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      { -        return getNameByID(self.get_id()); -    } +    public: +        Suspending(); +        ~Suspending(); -    /// getName() by self.get_id() -    std::string getNameByID(const void* self_id) const; +    private: +        coro::self* mSuspended; +    };      /// for delayed initialization      void setStackSize(S32 stacksize);  private: -    friend class LLSingleton<LLCoros>;      LLCoros(); -    std::string launchImpl(const std::string& prefix, coro* newCoro); +    friend class LLSingleton<LLCoros>;      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 81cc33fbba..be93e9c83b 100755 --- a/indra/llcommon/lleventcoro.cpp +++ b/indra/llcommon/lleventcoro.cpp @@ -34,33 +34,24 @@  #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::listenerNameForCoroImpl(const void* self_id) +std::string LLEventDetail::listenerNameForCoro()  { -    // First, if this coroutine was launched by LLCoros::launch(), find that name. -    std::string name(LLCoros::instance().getNameByID(self_id)); +    // If this coroutine was launched by LLCoros::launch(), find that name. +    std::string name(LLCoros::instance().getName());      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"); -    memo[self_id] = name; -    LL_INFOS("LLEventCoro") << "listenerNameForCoroImpl(" << self_id << "): inventing coro name '" +    LL_INFOS("LLEventCoro") << "listenerNameForCoro(): inventing coro name '"                              << name << "'" << LL_ENDL;      return name;  } @@ -118,6 +109,98 @@ 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 abbeeaa373..daf9360a2e 100755 --- a/indra/llcommon/lleventcoro.h +++ b/indra/llcommon/lleventcoro.h @@ -29,8 +29,6 @@  #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> @@ -102,9 +100,6 @@ 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 @@ -116,21 +111,9 @@ 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 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. +     * each distinct coroutine instance.       */ -    template <typename COROUTINE_SELF> -    std::string listenerNameForCoro(COROUTINE_SELF& self) -    { -        return listenerNameForCoroImpl(self.get_id()); -    } +    std::string listenerNameForCoro();      /**       * Implement behavior described for postAndWait()'s @a replyPumpNamePath @@ -159,7 +142,7 @@ namespace LLEventDetail   * convenience: the difference between this function and the sequence   * @code   * requestPump.post(myEvent); - * LLSD reply = waitForEventOn(self, replyPump); + * LLSD reply = waitForEventOn(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 @@ -201,51 +184,16 @@ namespace LLEventDetail   *   @a replyPumpNamePath specifies the entry in the lowest-level structure in   *   @a event into which to store <tt>replyPump.getName()</tt>.   */ -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; -} +LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump, +                 const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD());  /// Wait for the next event on the specified LLEventPump. Pass either the  /// LLEventPump& or its string name. -template <typename SELF> -LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump) +inline +LLSD waitForEventOn(const LLEventPumpOrPumpName& pump)  {      // This is now a convenience wrapper for postAndWait(). -    return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump); +    return postAndWait(LLSD(), LLEventPumpOrPumpName(), pump);  }  /// return type for two-pump variant of waitForEventOn() @@ -313,7 +261,7 @@ namespace LLEventDetail   * I'd have preferred to overload the name postAndWait() for both signatures.   * But consider the following ambiguous call:   * @code - * postAndWait(self, LLSD(), requestPump, replyPump, "someString"); + * postAndWait(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 @@ -322,69 +270,29 @@ namespace LLEventDetail   * It seems less burdensome to write postAndWait2() than to write either   * LLSD("someString") or LLEventOrPumpName("someString").   */ -template <typename SELF> -LLEventWithID postAndWait2(SELF& self, const LLSD& event, +LLEventWithID postAndWait2(const LLSD& event,                             const LLEventPumpOrPumpName& requestPump,                             const LLEventPumpOrPumpName& replyPump0,                             const LLEventPumpOrPumpName& replyPump1,                             const LLSD& replyPump0NamePath=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; -} +                           const LLSD& replyPump1NamePath=LLSD());  /**   * Wait for the next event on either of two specified LLEventPumps.   */ -template <typename SELF> +inline  LLEventWithID -waitForEventOn(SELF& self, -               const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1) +waitForEventOn(const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)  {      // This is now a convenience wrapper for postAndWait2(). -    return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1); +    return postAndWait2(LLSD(), LLEventPumpOrPumpName(), pump0, pump1);  }  /**   * Helper for the two-pump variant of waitForEventOn(), e.g.:   *   * @code - * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump), + * LLSD reply = errorException(waitForEventOn(replyPump, errorPump),   *                             "error response from login.cgi");   * @endcode   * @@ -454,26 +362,16 @@ 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.       */ -    template <typename SELF> -    LLSD wait(SELF& self) +    LLSD wait()      { -        return waitForEventOn(self, mPump); +        return ::waitForEventOn(mPump);      } -    template <typename SELF> -    LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump, +    LLSD postAndWait(const LLSD& event, const LLEventPumpOrPumpName& requestPump,                       const LLSD& replyPumpNamePath=LLSD())      { -        return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath); +        return ::postAndWait(event, requestPump, mPump, replyPumpNamePath);      }  private: @@ -509,55 +407,49 @@ public:      /// request pump 1      LLEventPump& getPump1() { return mPump1; } -    /// waitForEventOn(self, either of our two LLEventPumps) -    template <typename SELF> -    LLEventWithID wait(SELF& self) +    /// waitForEventOn(either of our two LLEventPumps) +    LLEventWithID wait()      { -        return waitForEventOn(self, mPump0, mPump1); +        return waitForEventOn(mPump0, mPump1);      } -    /// errorException(wait(self)) -    template <typename SELF> -    LLSD waitWithException(SELF& self) +    /// errorException(wait()) +    LLSD waitWithException()      { -        return errorException(wait(self), std::string("Error event on ") + getName1()); +        return errorException(wait(), std::string("Error event on ") + getName1());      } -    /// errorLog(wait(self)) -    template <typename SELF> -    LLSD waitWithLog(SELF& self) +    /// errorLog(wait()) +    LLSD waitWithLog()      { -        return errorLog(wait(self), std::string("Error event on ") + getName1()); +        return errorLog(wait(), std::string("Error event on ") + getName1());      } -    template <typename SELF> -    LLEventWithID postAndWait(SELF& self, const LLSD& event, +    LLEventWithID postAndWait(const LLSD& event,                                const LLEventPumpOrPumpName& requestPump,                                const LLSD& replyPump0NamePath=LLSD(),                                const LLSD& replyPump1NamePath=LLSD())      { -        return postAndWait2(self, event, requestPump, mPump0, mPump1, +        return postAndWait2(event, requestPump, mPump0, mPump1,                              replyPump0NamePath, replyPump1NamePath);      } -    template <typename SELF> -    LLSD postAndWaitWithException(SELF& self, const LLSD& event, +    LLSD postAndWaitWithException(const LLSD& event,                                    const LLEventPumpOrPumpName& requestPump,                                    const LLSD& replyPump0NamePath=LLSD(),                                    const LLSD& replyPump1NamePath=LLSD())      { -        return errorException(postAndWait(self, event, requestPump, +        return errorException(postAndWait(event, requestPump,                                            replyPump0NamePath, replyPump1NamePath),                                std::string("Error event on ") + getName1());      } -    template <typename SELF> -    LLSD postAndWaitWithLog(SELF& self, const LLSD& event, +    LLSD postAndWaitWithLog(const LLSD& event,                              const LLEventPumpOrPumpName& requestPump,                              const LLSD& replyPump0NamePath=LLSD(),                              const LLSD& replyPump1NamePath=LLSD())      { -        return errorLog(postAndWait(self, event, requestPump, +        return errorLog(postAndWait(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 2096807e53..da927038ab 100755 --- a/indra/llcommon/tests/lleventcoro_test.cpp +++ b/indra/llcommon/tests/lleventcoro_test.cpp @@ -82,6 +82,7 @@  #include "llevents.h"  #include "tests/wrapllerrs.h"  #include "stringize.h" +#include "llcoros.h"  #include "lleventcoro.h"  #include "../test/debug.h" @@ -121,9 +122,6 @@ 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 @@ -162,306 +160,7 @@ private:  *****************************************************************************/  namespace tut  { -    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; -    }; +    struct coroutine_data {};      typedef test_group<coroutine_data> coroutine_group;      typedef coroutine_group::object object;      coroutine_group coroutinegrp("coroutine"); @@ -511,16 +210,57 @@ 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. -        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. @@ -528,37 +268,56 @@ namespace tut          // Satisfy the wait.          LLEventPumps::instance().obtain("source").post("received");          // Now wait for the coroutine to complete. -        ensure_done(coro); +        ensure("coroutine complete", ! 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; -        coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn1, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<3>", waitForEventOn1);          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; -        coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<4>", waitForEventOn2);          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); @@ -567,43 +326,65 @@ namespace tut      template<> template<>      void object::test<5>()      { +        clear();          set_test_name("waitForEventOn2 error");          DEBUG; -        coroutine_type coro(boost::bind(&coroutine_data::waitForEventOn2, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<5>", waitForEventOn2);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPump, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<6>", coroPump);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<7>", coroPumps);          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);      } @@ -611,188 +392,389 @@ namespace tut      template<> template<>      void object::test<8>()      { +        clear();          set_test_name("coroPumps error");          DEBUG; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumps, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<8>", coroPumps);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoEx, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<9>", coroPumpsNoEx);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsEx, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<10>", coroPumpsEx);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsNoLog, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<11>", coroPumpsNoLog);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsLog, this, _1)); -        coro(std::nothrow); +        LLCoros::instance().launch("test<12>", coroPumpsLog);          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; -        coroutine_type coro(boost::bind(&coroutine_data::postAndWait1, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<13>", postAndWait1);          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; -        coroutine_type coro(boost::bind(&coroutine_data::postAndWait2, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<14>", postAndWait2);          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; -        coroutine_type coro(boost::bind(&coroutine_data::postAndWait2_1, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<15>", postAndWait2_1);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpPost, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<16>", coroPumpPost);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<17>", coroPumpsPost);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPost_1, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<18>", coroPumpsPost_1);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoEx, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<19>", coroPumpsPostNoEx);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostEx, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<20>", coroPumpsPostEx);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostNoLog, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<21>", coroPumpsPostNoLog);          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; -        coroutine_type coro(boost::bind(&coroutine_data::coroPumpsPostLog, this, _1)); -        coro(std::nothrow); -        ensure_done(coro); +        LLCoros::instance().launch("test<22>", coroPumpsPostLog);          ensure("no result", result.isUndefined());          ensure_contains("got error", threw, "32");      }  | 
