lleventcoro.h
上传用户:king477883
上传日期:2021-03-01
资源大小:9553k
文件大小:24k
源码类别:

游戏引擎

开发平台:

C++ Builder

  1. /**
  2.  * @file   lleventcoro.h
  3.  * @author Nat Goodspeed
  4.  * @date   2009-04-29
  5.  * @brief  Utilities to interface between coroutines and events.
  6.  * 
  7.  * $LicenseInfo:firstyear=2009&license=viewergpl$
  8.  * 
  9.  * Copyright (c) 2009-2010, Linden Research, Inc.
  10.  * 
  11.  * Second Life Viewer Source Code
  12.  * The source code in this file ("Source Code") is provided by Linden Lab
  13.  * to you under the terms of the GNU General Public License, version 2.0
  14.  * ("GPL"), unless you have obtained a separate licensing agreement
  15.  * ("Other License"), formally executed by you and Linden Lab.  Terms of
  16.  * the GPL can be found in doc/GPL-license.txt in this distribution, or
  17.  * online at http://secondlifegrid.net/programs/open_source/licensing/gplv2
  18.  * 
  19.  * There are special exceptions to the terms and conditions of the GPL as
  20.  * it is applied to this Source Code. View the full text of the exception
  21.  * in the file doc/FLOSS-exception.txt in this software distribution, or
  22.  * online at
  23.  * http://secondlifegrid.net/programs/open_source/licensing/flossexception
  24.  * 
  25.  * By copying, modifying or distributing this software, you acknowledge
  26.  * that you have read and understood your obligations described above,
  27.  * and agree to abide by those obligations.
  28.  * 
  29.  * ALL LINDEN LAB SOURCE CODE IS PROVIDED "AS IS." LINDEN LAB MAKES NO
  30.  * WARRANTIES, EXPRESS, IMPLIED OR OTHERWISE, REGARDING ITS ACCURACY,
  31.  * COMPLETENESS OR PERFORMANCE.
  32.  * $/LicenseInfo$
  33.  */
  34. #if ! defined(LL_LLEVENTCORO_H)
  35. #define LL_LLEVENTCORO_H
  36. #include <boost/coroutine/coroutine.hpp>
  37. #include <boost/coroutine/future.hpp>
  38. #include <boost/optional.hpp>
  39. #include <string>
  40. #include <stdexcept>
  41. #include "llevents.h"
  42. #include "llerror.h"
  43. /**
  44.  * Like LLListenerOrPumpName, this is a class intended for parameter lists:
  45.  * accept a <tt>const LLEventPumpOrPumpName&</tt> and you can accept either an
  46.  * <tt>LLEventPump&</tt> or its string name. For a single parameter that could
  47.  * be either, it's not hard to overload the function -- but as soon as you
  48.  * want to accept two such parameters, this is cheaper than four overloads.
  49.  */
  50. class LLEventPumpOrPumpName
  51. {
  52. public:
  53.     /// Pass an actual LLEventPump&
  54.     LLEventPumpOrPumpName(LLEventPump& pump):
  55.         mPump(pump)
  56.     {}
  57.     /// Pass the string name of an LLEventPump
  58.     LLEventPumpOrPumpName(const std::string& pumpname):
  59.         mPump(LLEventPumps::instance().obtain(pumpname))
  60.     {}
  61.     /// Pass string constant name of an LLEventPump. This override must be
  62.     /// explicit, since otherwise passing <tt>const char*</tt> to a function
  63.     /// accepting <tt>const LLEventPumpOrPumpName&</tt> would require two
  64.     /// different implicit conversions: <tt>const char*</tt> -> <tt>const
  65.     /// std::string&</tt> -> <tt>const LLEventPumpOrPumpName&</tt>.
  66.     LLEventPumpOrPumpName(const char* pumpname):
  67.         mPump(LLEventPumps::instance().obtain(pumpname))
  68.     {}
  69.     /// Unspecified: "I choose not to identify an LLEventPump."
  70.     LLEventPumpOrPumpName() {}
  71.     operator LLEventPump& () const { return *mPump; }
  72.     LLEventPump& getPump() const { return *mPump; }
  73.     operator bool() const { return mPump; }
  74.     bool operator!() const { return ! mPump; }
  75. private:
  76.     boost::optional<LLEventPump&> mPump;
  77. };
  78. /// This is an adapter for a signature like void LISTENER(const LLSD&), which
  79. /// isn't a valid LLEventPump listener: such listeners should return bool.
  80. template <typename LISTENER>
  81. class LLVoidListener
  82. {
  83. public:
  84.     LLVoidListener(const LISTENER& listener):
  85.         mListener(listener)
  86.     {}
  87.     bool operator()(const LLSD& event)
  88.     {
  89.         mListener(event);
  90.         // don't swallow the event, let other listeners see it
  91.         return false;
  92.     }
  93. private:
  94.     LISTENER mListener;
  95. };
  96. /// LLVoidListener helper function to infer the type of the LISTENER
  97. template <typename LISTENER>
  98. LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
  99. {
  100.     return LLVoidListener<LISTENER>(listener);
  101. }
  102. namespace LLEventDetail
  103. {
  104.     /**
  105.      * waitForEventOn() permits a coroutine to temporarily listen on an
  106.      * LLEventPump any number of times. We don't really want to have to ask
  107.      * the caller to label each such call with a distinct string; the whole
  108.      * point of waitForEventOn() is to present a nice sequential interface to
  109.      * the underlying LLEventPump-with-named-listeners machinery. So we'll use
  110.      * LLEventPump::inventName() to generate a distinct name for each
  111.      * temporary listener. On the other hand, because a given coroutine might
  112.      * call waitForEventOn() any number of times, we don't really want to
  113.      * consume an arbitrary number of generated inventName()s: that namespace,
  114.      * though large, is nonetheless finite. So we memoize an invented name for
  115.      * each distinct coroutine instance (each different 'self' object). We
  116.      * can't know the type of 'self', because it depends on the coroutine
  117.      * body's signature. So we cast its address to void*, looking for distinct
  118.      * pointer values. Yes, that means that an early coroutine could cache a
  119.      * value here, then be destroyed, only to be supplanted by a later
  120.      * coroutine (of the same or different type), and we'll end up
  121.      * "recognizing" the second one and reusing the listener name -- but
  122.      * that's okay, since it won't collide with any listener name used by the
  123.      * earlier coroutine since that earlier coroutine no longer exists.
  124.      */
  125.     template <typename COROUTINE_SELF>
  126.     std::string listenerNameForCoro(COROUTINE_SELF& self)
  127.     {
  128.         return listenerNameForCoroImpl(self.get_id());
  129.     }
  130.     /// Implementation for listenerNameForCoro()
  131.     LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
  132.     /**
  133.      * Implement behavior described for postAndWait()'s @a replyPumpNamePath
  134.      * parameter:
  135.      *
  136.      * * If <tt>path.isUndefined()</tt>, do nothing.
  137.      * * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
  138.      *   into <tt>dest[path.asString()]</tt>.
  139.      * * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
  140.      *   value into <tt>dest[path.asInteger()]</tt>.
  141.      * * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
  142.      *   down through the structure of @a dest. The last array entry in @a
  143.      *   path specifies the entry in the lowest-level structure in @a dest
  144.      *   into which to store @a value.
  145.      *
  146.      * @note
  147.      * In the degenerate case in which @a path is an empty array, @a dest will
  148.      * @em become @a value rather than @em containing it.
  149.      */
  150.     LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value);
  151. } // namespace LLEventDetail
  152. /**
  153.  * Post specified LLSD event on the specified LLEventPump, then wait for a
  154.  * response on specified other LLEventPump. This is more than mere
  155.  * convenience: the difference between this function and the sequence
  156.  * @code
  157.  * requestPump.post(myEvent);
  158.  * LLSD reply = waitForEventOn(self, replyPump);
  159.  * @endcode
  160.  * is that the sequence above fails if the reply is posted immediately on
  161.  * @a replyPump, that is, before <tt>requestPump.post()</tt> returns. In the
  162.  * sequence above, the running coroutine isn't even listening on @a replyPump
  163.  * until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is
  164.  * entered. Therefore, the coroutine completely misses an immediate reply
  165.  * event, making it wait indefinitely.
  166.  *
  167.  * By contrast, postAndWait() listens on the @a replyPump @em before posting
  168.  * the specified LLSD event on the specified @a requestPump.
  169.  *
  170.  * @param self The @c self object passed into a coroutine
  171.  * @param event LLSD data to be posted on @a requestPump
  172.  * @param requestPump an LLEventPump on which to post @a event. Pass either
  173.  * the LLEventPump& or its string name. However, if you pass a
  174.  * default-constructed @c LLEventPumpOrPumpName, we skip the post() call.
  175.  * @param replyPump an LLEventPump on which postAndWait() will listen for a
  176.  * reply. Pass either the LLEventPump& or its string name. The calling
  177.  * coroutine will wait until that reply arrives. (If you're concerned about a
  178.  * reply that might not arrive, please see also LLEventTimeout.)
  179.  * @param replyPumpNamePath specifies the location within @a event in which to
  180.  * store <tt>replyPump.getName()</tt>. This is a strictly optional convenience
  181.  * feature; obviously you can store the name in @a event "by hand" if desired.
  182.  * @a replyPumpNamePath can be specified in any of four forms:
  183.  * * @c isUndefined() (default-constructed LLSD object): do nothing. This is
  184.  *   the default behavior if you omit @a replyPumpNamePath.
  185.  * * @c isInteger(): @a event is an array. Store <tt>replyPump.getName()</tt>
  186.  *   in <tt>event[replyPumpNamePath.asInteger()]</tt>.
  187.  * * @c isString(): @a event is a map. Store <tt>replyPump.getName()</tt> in
  188.  *   <tt>event[replyPumpNamePath.asString()]</tt>.
  189.  * * @c isArray(): @a event has several levels of structure, e.g. map of
  190.  *   maps, array of arrays, array of maps, map of arrays, ... Store
  191.  *   <tt>replyPump.getName()</tt> in
  192.  *   <tt>event[replyPumpNamePath[0]][replyPumpNamePath[1]]...</tt> In other
  193.  *   words, examine each array entry in @a replyPumpNamePath in turn. If it's an
  194.  *   <tt>LLSD::String</tt>, the current level of @a event is a map; step down to
  195.  *   that map entry. If it's an <tt>LLSD::Integer</tt>, the current level of @a
  196.  *   event is an array; step down to that array entry. The last array entry in
  197.  *   @a replyPumpNamePath specifies the entry in the lowest-level structure in
  198.  *   @a event into which to store <tt>replyPump.getName()</tt>.
  199.  */
  200. template <typename SELF>
  201. LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
  202.                  const LLEventPumpOrPumpName& replyPump, const LLSD& replyPumpNamePath=LLSD())
  203. {
  204.     // declare the future
  205.     boost::coroutines::future<LLSD> future(self);
  206.     // make a callback that will assign a value to the future, and listen on
  207.     // the specified LLEventPump with that callback
  208.     std::string listenerName(LLEventDetail::listenerNameForCoro(self));
  209.     LLTempBoundListener connection(
  210.         replyPump.getPump().listen(listenerName,
  211.                                    voidlistener(boost::coroutines::make_callback(future))));
  212.     // skip the "post" part if requestPump is default-constructed
  213.     if (requestPump)
  214.     {
  215.         // If replyPumpNamePath is non-empty, store the replyPump name in the
  216.         // request event.
  217.         LLSD modevent(event);
  218.         LLEventDetail::storeToLLSDPath(modevent, replyPumpNamePath, replyPump.getPump().getName());
  219. LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
  220.                                  << " posting to " << requestPump.getPump().getName()
  221.  << LL_ENDL;
  222. // *NOTE:Mani - Removed because modevent could contain user's hashed passwd.
  223. //                         << ": " << modevent << LL_ENDL;
  224.         requestPump.getPump().post(modevent);
  225.     }
  226.     LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
  227.                              << " about to wait on LLEventPump " << replyPump.getPump().getName()
  228.                              << LL_ENDL;
  229.     // trying to dereference ("resolve") the future makes us wait for it
  230.     LLSD value(*future);
  231.     LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << listenerName
  232.                              << " resuming with " << value << LL_ENDL;
  233.     // returning should disconnect the connection
  234.     return value;
  235. }
  236. /// Wait for the next event on the specified LLEventPump. Pass either the
  237. /// LLEventPump& or its string name.
  238. template <typename SELF>
  239. LLSD waitForEventOn(SELF& self, const LLEventPumpOrPumpName& pump)
  240. {
  241.     // This is now a convenience wrapper for postAndWait().
  242.     return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
  243. }
  244. /// return type for two-pump variant of waitForEventOn()
  245. typedef std::pair<LLSD, int> LLEventWithID;
  246. namespace LLEventDetail
  247. {
  248.     /**
  249.      * This helper is specifically for the two-pump version of waitForEventOn().
  250.      * We use a single future object, but we want to listen on two pumps with it.
  251.      * Since we must still adapt from (the callable constructed by)
  252.      * boost::coroutines::make_callback() (void return) to provide an event
  253.      * listener (bool return), we've adapted LLVoidListener for the purpose. The
  254.      * basic idea is that we construct a distinct instance of WaitForEventOnHelper
  255.      * -- binding different instance data -- for each of the pumps. Then, when a
  256.      * pump delivers an LLSD value to either WaitForEventOnHelper, it can combine
  257.      * that LLSD with its discriminator to feed the future object.
  258.      */
  259.     template <typename LISTENER>
  260.     class WaitForEventOnHelper
  261.     {
  262.     public:
  263.         WaitForEventOnHelper(const LISTENER& listener, int discriminator):
  264.             mListener(listener),
  265.             mDiscrim(discriminator)
  266.         {}
  267.         // this signature is required for an LLEventPump listener
  268.         bool operator()(const LLSD& event)
  269.         {
  270.             // our future object is defined to accept LLEventWithID
  271.             mListener(LLEventWithID(event, mDiscrim));
  272.             // don't swallow the event, let other listeners see it
  273.             return false;
  274.         }
  275.     private:
  276.         LISTENER mListener;
  277.         const int mDiscrim;
  278.     };
  279.     /// WaitForEventOnHelper type-inference helper
  280.     template <typename LISTENER>
  281.     WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
  282.     {
  283.         return WaitForEventOnHelper<LISTENER>(listener, discriminator);
  284.     }
  285. } // namespace LLEventDetail
  286. /**
  287.  * This function waits for a reply on either of two specified LLEventPumps.
  288.  * Otherwise, it closely resembles postAndWait(); please see the documentation
  289.  * for that function for detailed parameter info.
  290.  *
  291.  * While we could have implemented the single-pump variant in terms of this
  292.  * one, there's enough added complexity here to make it worthwhile to give the
  293.  * single-pump variant its own straightforward implementation. Conversely,
  294.  * though we could use preprocessor logic to generate n-pump overloads up to
  295.  * BOOST_COROUTINE_WAIT_MAX, we don't foresee a use case. This two-pump
  296.  * overload exists because certain event APIs are defined in terms of a reply
  297.  * LLEventPump and an error LLEventPump.
  298.  *
  299.  * The LLEventWithID return value provides not only the received event, but
  300.  * the index of the pump on which it arrived (0 or 1).
  301.  *
  302.  * @note
  303.  * I'd have preferred to overload the name postAndWait() for both signatures.
  304.  * But consider the following ambiguous call:
  305.  * @code
  306.  * postAndWait(self, LLSD(), requestPump, replyPump, "someString");
  307.  * @endcode
  308.  * "someString" could be converted to either LLSD (@a replyPumpNamePath for
  309.  * the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
  310.  * function).
  311.  *
  312.  * It seems less burdensome to write postAndWait2() than to write either
  313.  * LLSD("someString") or LLEventOrPumpName("someString").
  314.  */
  315. template <typename SELF>
  316. LLEventWithID postAndWait2(SELF& self, const LLSD& event,
  317.                            const LLEventPumpOrPumpName& requestPump,
  318.                            const LLEventPumpOrPumpName& replyPump0,
  319.                            const LLEventPumpOrPumpName& replyPump1,
  320.                            const LLSD& replyPump0NamePath=LLSD(),
  321.                            const LLSD& replyPump1NamePath=LLSD())
  322. {
  323.     // declare the future
  324.     boost::coroutines::future<LLEventWithID> future(self);
  325.     // either callback will assign a value to this future; listen on
  326.     // each specified LLEventPump with a callback
  327.     std::string name(LLEventDetail::listenerNameForCoro(self));
  328.     LLTempBoundListener connection0(
  329.         replyPump0.getPump().listen(name + "a",
  330.                                LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 0)));
  331.     LLTempBoundListener connection1(
  332.         replyPump1.getPump().listen(name + "b",
  333.                                LLEventDetail::wfeoh(boost::coroutines::make_callback(future), 1)));
  334.     // skip the "post" part if requestPump is default-constructed
  335.     if (requestPump)
  336.     {
  337.         // If either replyPumpNamePath is non-empty, store the corresponding
  338.         // replyPump name in the request event.
  339.         LLSD modevent(event);
  340.         LLEventDetail::storeToLLSDPath(modevent, replyPump0NamePath,
  341.                                        replyPump0.getPump().getName());
  342.         LLEventDetail::storeToLLSDPath(modevent, replyPump1NamePath,
  343.                                        replyPump1.getPump().getName());
  344.         LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
  345.                                  << " posting to " << requestPump.getPump().getName()
  346.                                  << ": " << modevent << LL_ENDL;
  347.         requestPump.getPump().post(modevent);
  348.     }
  349.     LL_DEBUGS("lleventcoro") << "postAndWait2(): coroutine " << name
  350.                              << " about to wait on LLEventPumps " << replyPump0.getPump().getName()
  351.                              << ", " << replyPump1.getPump().getName() << LL_ENDL;
  352.     // trying to dereference ("resolve") the future makes us wait for it
  353.     LLEventWithID value(*future);
  354.     LL_DEBUGS("lleventcoro") << "postAndWait(): coroutine " << name
  355.                              << " resuming with (" << value.first << ", " << value.second << ")"
  356.                              << LL_ENDL;
  357.     // returning should disconnect both connections
  358.     return value;
  359. }
  360. /**
  361.  * Wait for the next event on either of two specified LLEventPumps.
  362.  */
  363. template <typename SELF>
  364. LLEventWithID
  365. waitForEventOn(SELF& self,
  366.                const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
  367. {
  368.     // This is now a convenience wrapper for postAndWait2().
  369.     return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
  370. }
  371. /**
  372.  * Helper for the two-pump variant of waitForEventOn(), e.g.:
  373.  *
  374.  * @code
  375.  * LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
  376.  *                             "error response from login.cgi");
  377.  * @endcode
  378.  *
  379.  * Examines an LLEventWithID, assuming that the second pump (pump 1) is
  380.  * listening for an error indication. If the incoming data arrived on pump 1,
  381.  * throw an LLErrorEvent exception. If the incoming data arrived on pump 0,
  382.  * just return it. Since a normal return can only be from pump 0, we no longer
  383.  * need the LLEventWithID's discriminator int; we can just return the LLSD.
  384.  *
  385.  * @note I'm not worried about introducing the (fairly generic) name
  386.  * errorException() into global namespace, because how many other overloads of
  387.  * the same name are going to accept an LLEventWithID parameter?
  388.  */
  389. LLSD errorException(const LLEventWithID& result, const std::string& desc);
  390. /**
  391.  * Exception thrown by errorException(). We don't call this LLEventError
  392.  * because it's not an error in event processing: rather, this exception
  393.  * announces an event that bears error information (for some other API).
  394.  */
  395. class LL_COMMON_API LLErrorEvent: public std::runtime_error
  396. {
  397. public:
  398.     LLErrorEvent(const std::string& what, const LLSD& data):
  399.         std::runtime_error(what),
  400.         mData(data)
  401.     {}
  402.     virtual ~LLErrorEvent() throw() {}
  403.     LLSD getData() const { return mData; }
  404. private:
  405.     LLSD mData;
  406. };
  407. /**
  408.  * Like errorException(), save that this trips a fatal error using LL_ERRS
  409.  * rather than throwing an exception.
  410.  */
  411. LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc);
  412. /**
  413.  * Certain event APIs require the name of an LLEventPump on which they should
  414.  * post results. While it works to invent a distinct name and let
  415.  * LLEventPumps::obtain() instantiate the LLEventPump as a "named singleton,"
  416.  * in a certain sense it's more robust to instantiate a local LLEventPump and
  417.  * provide its name instead. This class packages the following idiom:
  418.  *
  419.  * 1. Instantiate a local LLCoroEventPump, with an optional name prefix.
  420.  * 2. Provide its actual name to the event API in question as the name of the
  421.  *    reply LLEventPump.
  422.  * 3. Initiate the request to the event API.
  423.  * 4. Call your LLEventTempStream's wait() method to wait for the reply.
  424.  * 5. Let the LLCoroEventPump go out of scope.
  425.  */
  426. class LL_COMMON_API LLCoroEventPump
  427. {
  428. public:
  429.     LLCoroEventPump(const std::string& name="coro"):
  430.         mPump(name, true)           // allow tweaking the pump instance name
  431.     {}
  432.     /// It's typical to request the LLEventPump name to direct an event API to
  433.     /// send its response to this pump.
  434.     std::string getName() const { return mPump.getName(); }
  435.     /// Less typically, we'd request the pump itself for some reason.
  436.     LLEventPump& getPump() { return mPump; }
  437.     /**
  438.      * Wait for an event on this LLEventPump.
  439.      *
  440.      * @note
  441.      * The other major usage pattern we considered was to bind @c self at
  442.      * LLCoroEventPump construction time, which would avoid passing the
  443.      * parameter to each wait() call. But if we were going to bind @c self as
  444.      * a class member, we'd need to specify a class template parameter
  445.      * indicating its type. The big advantage of passing it to the wait() call
  446.      * is that the type can be implicit.
  447.      */
  448.     template <typename SELF>
  449.     LLSD wait(SELF& self)
  450.     {
  451.         return waitForEventOn(self, mPump);
  452.     }
  453.     template <typename SELF>
  454.     LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
  455.                      const LLSD& replyPumpNamePath=LLSD())
  456.     {
  457.         return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
  458.     }
  459. private:
  460.     LLEventStream mPump;
  461. };
  462. /**
  463.  * Other event APIs require the names of two different LLEventPumps: one for
  464.  * success response, the other for error response. Extend LLCoroEventPump
  465.  * for the two-pump use case.
  466.  */
  467. class LL_COMMON_API LLCoroEventPumps
  468. {
  469. public:
  470.     LLCoroEventPumps(const std::string& name="coro",
  471.                      const std::string& suff0="Reply",
  472.                      const std::string& suff1="Error"):
  473.         mPump0(name + suff0, true),   // allow tweaking the pump instance name
  474.         mPump1(name + suff1, true)
  475.     {}
  476.     /// request pump 0's name
  477.     std::string getName0() const { return mPump0.getName(); }
  478.     /// request pump 1's name
  479.     std::string getName1() const { return mPump1.getName(); }
  480.     /// request both names
  481.     std::pair<std::string, std::string> getNames() const
  482.     {
  483.         return std::pair<std::string, std::string>(mPump0.getName(), mPump1.getName());
  484.     }
  485.     /// request pump 0
  486.     LLEventPump& getPump0() { return mPump0; }
  487.     /// request pump 1
  488.     LLEventPump& getPump1() { return mPump1; }
  489.     /// waitForEventOn(self, either of our two LLEventPumps)
  490.     template <typename SELF>
  491.     LLEventWithID wait(SELF& self)
  492.     {
  493.         return waitForEventOn(self, mPump0, mPump1);
  494.     }
  495.     /// errorException(wait(self))
  496.     template <typename SELF>
  497.     LLSD waitWithException(SELF& self)
  498.     {
  499.         return errorException(wait(self), std::string("Error event on ") + getName1());
  500.     }
  501.     /// errorLog(wait(self))
  502.     template <typename SELF>
  503.     LLSD waitWithLog(SELF& self)
  504.     {
  505.         return errorLog(wait(self), std::string("Error event on ") + getName1());
  506.     }
  507.     template <typename SELF>
  508.     LLEventWithID postAndWait(SELF& self, const LLSD& event,
  509.                               const LLEventPumpOrPumpName& requestPump,
  510.                               const LLSD& replyPump0NamePath=LLSD(),
  511.                               const LLSD& replyPump1NamePath=LLSD())
  512.     {
  513.         return postAndWait2(self, event, requestPump, mPump0, mPump1,
  514.                             replyPump0NamePath, replyPump1NamePath);
  515.     }
  516.     template <typename SELF>
  517.     LLSD postAndWaitWithException(SELF& self, const LLSD& event,
  518.                                   const LLEventPumpOrPumpName& requestPump,
  519.                                   const LLSD& replyPump0NamePath=LLSD(),
  520.                                   const LLSD& replyPump1NamePath=LLSD())
  521.     {
  522.         return errorException(postAndWait(self, event, requestPump,
  523.                                           replyPump0NamePath, replyPump1NamePath),
  524.                               std::string("Error event on ") + getName1());
  525.     }
  526.     template <typename SELF>
  527.     LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
  528.                             const LLEventPumpOrPumpName& requestPump,
  529.                             const LLSD& replyPump0NamePath=LLSD(),
  530.                             const LLSD& replyPump1NamePath=LLSD())
  531.     {
  532.         return errorLog(postAndWait(self, event, requestPump,
  533.                                     replyPump0NamePath, replyPump1NamePath),
  534.                         std::string("Error event on ") + getName1());
  535.     }
  536. private:
  537.     LLEventStream mPump0, mPump1;
  538. };
  539. #endif /* ! defined(LL_LLEVENTCORO_H) */