570 lines
24 KiB
C++
570 lines
24 KiB
C++
/**
|
|
* @file lleventcoro.h
|
|
* @author Nat Goodspeed
|
|
* @date 2009-04-29
|
|
* @brief Utilities to interface between coroutines and events.
|
|
*
|
|
* $LicenseInfo:firstyear=2009&license=viewerlgpl$
|
|
* Second Life Viewer Source Code
|
|
* Copyright (C) 2010, Linden Research, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation;
|
|
* version 2.1 of the License only.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*
|
|
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
|
* $/LicenseInfo$
|
|
*/
|
|
|
|
#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>
|
|
#include "llevents.h"
|
|
#include "llerror.h"
|
|
|
|
/**
|
|
* Like LLListenerOrPumpName, this is a class intended for parameter lists:
|
|
* accept a <tt>const LLEventPumpOrPumpName&</tt> and you can accept either an
|
|
* <tt>LLEventPump&</tt> or its string name. For a single parameter that could
|
|
* be either, it's not hard to overload the function -- but as soon as you
|
|
* want to accept two such parameters, this is cheaper than four overloads.
|
|
*/
|
|
class LLEventPumpOrPumpName
|
|
{
|
|
public:
|
|
/// Pass an actual LLEventPump&
|
|
LLEventPumpOrPumpName(LLEventPump& pump):
|
|
mPump(pump)
|
|
{}
|
|
/// Pass the string name of an LLEventPump
|
|
LLEventPumpOrPumpName(const std::string& pumpname):
|
|
mPump(LLEventPumps::instance().obtain(pumpname))
|
|
{}
|
|
/// Pass string constant name of an LLEventPump. This override must be
|
|
/// explicit, since otherwise passing <tt>const char*</tt> to a function
|
|
/// accepting <tt>const LLEventPumpOrPumpName&</tt> would require two
|
|
/// different implicit conversions: <tt>const char*</tt> -> <tt>const
|
|
/// std::string&</tt> -> <tt>const LLEventPumpOrPumpName&</tt>.
|
|
LLEventPumpOrPumpName(const char* pumpname):
|
|
mPump(LLEventPumps::instance().obtain(pumpname))
|
|
{}
|
|
/// Unspecified: "I choose not to identify an LLEventPump."
|
|
LLEventPumpOrPumpName() {}
|
|
operator LLEventPump& () const { return *mPump; }
|
|
LLEventPump& getPump() const { return *mPump; }
|
|
operator bool() const { return !!mPump; }
|
|
bool operator!() const { return ! mPump; }
|
|
|
|
private:
|
|
boost::optional<LLEventPump&> mPump;
|
|
};
|
|
|
|
/// This is an adapter for a signature like void LISTENER(const LLSD&), which
|
|
/// isn't a valid LLEventPump listener: such listeners should return bool.
|
|
template <typename LISTENER>
|
|
class LLVoidListener
|
|
{
|
|
public:
|
|
LLVoidListener(const LISTENER& listener):
|
|
mListener(listener)
|
|
{}
|
|
bool operator()(const LLSD& event)
|
|
{
|
|
mListener(event);
|
|
// don't swallow the event, let other listeners see it
|
|
return false;
|
|
}
|
|
private:
|
|
LISTENER mListener;
|
|
};
|
|
|
|
/// LLVoidListener helper function to infer the type of the LISTENER
|
|
template <typename LISTENER>
|
|
LLVoidListener<LISTENER> voidlistener(const LISTENER& listener)
|
|
{
|
|
return LLVoidListener<LISTENER>(listener);
|
|
}
|
|
|
|
namespace LLEventDetail
|
|
{
|
|
/**
|
|
* waitForEventOn() permits a coroutine to temporarily listen on an
|
|
* LLEventPump any number of times. We don't really want to have to ask
|
|
* the caller to label each such call with a distinct string; the whole
|
|
* point of waitForEventOn() is to present a nice sequential interface to
|
|
* the underlying LLEventPump-with-named-listeners machinery. So we'll use
|
|
* LLEventPump::inventName() to generate a distinct name for each
|
|
* temporary listener. On the other hand, because a given coroutine might
|
|
* call waitForEventOn() any number of times, we don't really want to
|
|
* consume an arbitrary number of generated inventName()s: that namespace,
|
|
* though large, is nonetheless finite. So we memoize an invented name for
|
|
* each distinct coroutine instance (each different 'self' object). We
|
|
* can't know the type of 'self', because it depends on the coroutine
|
|
* body's signature. So we cast its address to void*, looking for distinct
|
|
* pointer values. Yes, that means that an early coroutine could cache a
|
|
* 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.
|
|
*/
|
|
template <typename COROUTINE_SELF>
|
|
std::string listenerNameForCoro(COROUTINE_SELF& self)
|
|
{
|
|
return listenerNameForCoroImpl(self.get_id());
|
|
}
|
|
|
|
/// Implementation for listenerNameForCoro()
|
|
LL_COMMON_API std::string listenerNameForCoroImpl(const void* self_id);
|
|
|
|
/**
|
|
* Implement behavior described for postAndWait()'s @a replyPumpNamePath
|
|
* parameter:
|
|
*
|
|
* * If <tt>path.isUndefined()</tt>, do nothing.
|
|
* * If <tt>path.isString()</tt>, @a dest is an LLSD map: store @a value
|
|
* into <tt>dest[path.asString()]</tt>.
|
|
* * If <tt>path.isInteger()</tt>, @a dest is an LLSD array: store @a
|
|
* value into <tt>dest[path.asInteger()]</tt>.
|
|
* * If <tt>path.isArray()</tt>, iteratively apply the rules above to step
|
|
* down through the structure of @a dest. The last array entry in @a
|
|
* path specifies the entry in the lowest-level structure in @a dest
|
|
* into which to store @a value.
|
|
*
|
|
* @note
|
|
* In the degenerate case in which @a path is an empty array, @a dest will
|
|
* @em become @a value rather than @em containing it.
|
|
*/
|
|
LL_COMMON_API void storeToLLSDPath(LLSD& dest, const LLSD& path, const LLSD& value);
|
|
} // namespace LLEventDetail
|
|
|
|
/**
|
|
* Post specified LLSD event on the specified LLEventPump, then wait for a
|
|
* response on specified other LLEventPump. This is more than mere
|
|
* convenience: the difference between this function and the sequence
|
|
* @code
|
|
* requestPump.post(myEvent);
|
|
* 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
|
|
* sequence above, the running coroutine isn't even listening on @a replyPump
|
|
* until <tt>requestPump.post()</tt> returns and @c waitForEventOn() is
|
|
* entered. Therefore, the coroutine completely misses an immediate reply
|
|
* event, making it wait indefinitely.
|
|
*
|
|
* By contrast, postAndWait() listens on the @a replyPump @em before posting
|
|
* the specified LLSD event on the specified @a requestPump.
|
|
*
|
|
* @param self The @c self object passed into a coroutine
|
|
* @param event LLSD data to be posted on @a requestPump
|
|
* @param requestPump an LLEventPump on which to post @a event. Pass either
|
|
* the LLEventPump& or its string name. However, if you pass a
|
|
* default-constructed @c LLEventPumpOrPumpName, we skip the post() call.
|
|
* @param replyPump an LLEventPump on which postAndWait() will listen for a
|
|
* reply. Pass either the LLEventPump& or its string name. The calling
|
|
* coroutine will wait until that reply arrives. (If you're concerned about a
|
|
* reply that might not arrive, please see also LLEventTimeout.)
|
|
* @param replyPumpNamePath specifies the location within @a event in which to
|
|
* store <tt>replyPump.getName()</tt>. This is a strictly optional convenience
|
|
* feature; obviously you can store the name in @a event "by hand" if desired.
|
|
* @a replyPumpNamePath can be specified in any of four forms:
|
|
* * @c isUndefined() (default-constructed LLSD object): do nothing. This is
|
|
* the default behavior if you omit @a replyPumpNamePath.
|
|
* * @c isInteger(): @a event is an array. Store <tt>replyPump.getName()</tt>
|
|
* in <tt>event[replyPumpNamePath.asInteger()]</tt>.
|
|
* * @c isString(): @a event is a map. Store <tt>replyPump.getName()</tt> in
|
|
* <tt>event[replyPumpNamePath.asString()]</tt>.
|
|
* * @c isArray(): @a event has several levels of structure, e.g. map of
|
|
* maps, array of arrays, array of maps, map of arrays, ... Store
|
|
* <tt>replyPump.getName()</tt> in
|
|
* <tt>event[replyPumpNamePath[0]][replyPumpNamePath[1]]...</tt> In other
|
|
* words, examine each array entry in @a replyPumpNamePath in turn. If it's an
|
|
* <tt>LLSD::String</tt>, the current level of @a event is a map; step down to
|
|
* that map entry. If it's an <tt>LLSD::Integer</tt>, the current level of @a
|
|
* event is an array; step down to that array entry. The last array entry in
|
|
* @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;
|
|
}
|
|
|
|
/// 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)
|
|
{
|
|
// This is now a convenience wrapper for postAndWait().
|
|
return postAndWait(self, LLSD(), LLEventPumpOrPumpName(), pump);
|
|
}
|
|
|
|
/// return type for two-pump variant of waitForEventOn()
|
|
typedef std::pair<LLSD, int> LLEventWithID;
|
|
|
|
namespace LLEventDetail
|
|
{
|
|
/**
|
|
* This helper is specifically for the two-pump version of waitForEventOn().
|
|
* We use a single future object, but we want to listen on two pumps with it.
|
|
* Since we must still adapt from (the callable constructed by)
|
|
* boost::dcoroutines::make_callback() (void return) to provide an event
|
|
* listener (bool return), we've adapted LLVoidListener for the purpose. The
|
|
* basic idea is that we construct a distinct instance of WaitForEventOnHelper
|
|
* -- binding different instance data -- for each of the pumps. Then, when a
|
|
* pump delivers an LLSD value to either WaitForEventOnHelper, it can combine
|
|
* that LLSD with its discriminator to feed the future object.
|
|
*/
|
|
template <typename LISTENER>
|
|
class WaitForEventOnHelper
|
|
{
|
|
public:
|
|
WaitForEventOnHelper(const LISTENER& listener, int discriminator):
|
|
mListener(listener),
|
|
mDiscrim(discriminator)
|
|
{}
|
|
// this signature is required for an LLEventPump listener
|
|
bool operator()(const LLSD& event)
|
|
{
|
|
// our future object is defined to accept LLEventWithID
|
|
mListener(LLEventWithID(event, mDiscrim));
|
|
// don't swallow the event, let other listeners see it
|
|
return false;
|
|
}
|
|
private:
|
|
LISTENER mListener;
|
|
const int mDiscrim;
|
|
};
|
|
|
|
/// WaitForEventOnHelper type-inference helper
|
|
template <typename LISTENER>
|
|
WaitForEventOnHelper<LISTENER> wfeoh(const LISTENER& listener, int discriminator)
|
|
{
|
|
return WaitForEventOnHelper<LISTENER>(listener, discriminator);
|
|
}
|
|
} // namespace LLEventDetail
|
|
|
|
/**
|
|
* This function waits for a reply on either of two specified LLEventPumps.
|
|
* Otherwise, it closely resembles postAndWait(); please see the documentation
|
|
* for that function for detailed parameter info.
|
|
*
|
|
* While we could have implemented the single-pump variant in terms of this
|
|
* one, there's enough added complexity here to make it worthwhile to give the
|
|
* single-pump variant its own straightforward implementation. Conversely,
|
|
* though we could use preprocessor logic to generate n-pump overloads up to
|
|
* BOOST_COROUTINE_WAIT_MAX, we don't foresee a use case. This two-pump
|
|
* overload exists because certain event APIs are defined in terms of a reply
|
|
* LLEventPump and an error LLEventPump.
|
|
*
|
|
* The LLEventWithID return value provides not only the received event, but
|
|
* the index of the pump on which it arrived (0 or 1).
|
|
*
|
|
* @note
|
|
* 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");
|
|
* @endcode
|
|
* "someString" could be converted to either LLSD (@a replyPumpNamePath for
|
|
* the single-pump function) or LLEventOrPumpName (@a replyPump1 for two-pump
|
|
* function).
|
|
*
|
|
* 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,
|
|
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;
|
|
}
|
|
|
|
/**
|
|
* Wait for the next event on either of two specified LLEventPumps.
|
|
*/
|
|
template <typename SELF>
|
|
LLEventWithID
|
|
waitForEventOn(SELF& self,
|
|
const LLEventPumpOrPumpName& pump0, const LLEventPumpOrPumpName& pump1)
|
|
{
|
|
// This is now a convenience wrapper for postAndWait2().
|
|
return postAndWait2(self, LLSD(), LLEventPumpOrPumpName(), pump0, pump1);
|
|
}
|
|
|
|
/**
|
|
* Helper for the two-pump variant of waitForEventOn(), e.g.:
|
|
*
|
|
* @code
|
|
* LLSD reply = errorException(waitForEventOn(self, replyPump, errorPump),
|
|
* "error response from login.cgi");
|
|
* @endcode
|
|
*
|
|
* Examines an LLEventWithID, assuming that the second pump (pump 1) is
|
|
* listening for an error indication. If the incoming data arrived on pump 1,
|
|
* throw an LLErrorEvent exception. If the incoming data arrived on pump 0,
|
|
* just return it. Since a normal return can only be from pump 0, we no longer
|
|
* need the LLEventWithID's discriminator int; we can just return the LLSD.
|
|
*
|
|
* @note I'm not worried about introducing the (fairly generic) name
|
|
* errorException() into global namespace, because how many other overloads of
|
|
* the same name are going to accept an LLEventWithID parameter?
|
|
*/
|
|
LLSD errorException(const LLEventWithID& result, const std::string& desc);
|
|
|
|
/**
|
|
* Exception thrown by errorException(). We don't call this LLEventError
|
|
* because it's not an error in event processing: rather, this exception
|
|
* announces an event that bears error information (for some other API).
|
|
*/
|
|
class LL_COMMON_API LLErrorEvent: public std::runtime_error
|
|
{
|
|
public:
|
|
LLErrorEvent(const std::string& what, const LLSD& data):
|
|
std::runtime_error(what),
|
|
mData(data)
|
|
{}
|
|
virtual ~LLErrorEvent() noexcept {}
|
|
|
|
LLSD getData() const { return mData; }
|
|
|
|
private:
|
|
LLSD mData;
|
|
};
|
|
|
|
/**
|
|
* Like errorException(), save that this trips a fatal error using LL_ERRS
|
|
* rather than throwing an exception.
|
|
*/
|
|
LL_COMMON_API LLSD errorLog(const LLEventWithID& result, const std::string& desc);
|
|
|
|
/**
|
|
* Certain event APIs require the name of an LLEventPump on which they should
|
|
* post results. While it works to invent a distinct name and let
|
|
* LLEventPumps::obtain() instantiate the LLEventPump as a "named singleton,"
|
|
* in a certain sense it's more robust to instantiate a local LLEventPump and
|
|
* provide its name instead. This class packages the following idiom:
|
|
*
|
|
* 1. Instantiate a local LLCoroEventPump, with an optional name prefix.
|
|
* 2. Provide its actual name to the event API in question as the name of the
|
|
* reply LLEventPump.
|
|
* 3. Initiate the request to the event API.
|
|
* 4. Call your LLEventTempStream's wait() method to wait for the reply.
|
|
* 5. Let the LLCoroEventPump go out of scope.
|
|
*/
|
|
class LL_COMMON_API LLCoroEventPump
|
|
{
|
|
public:
|
|
LLCoroEventPump(const std::string& name="coro"):
|
|
mPump(name, true) // allow tweaking the pump instance name
|
|
{}
|
|
/// It's typical to request the LLEventPump name to direct an event API to
|
|
/// send its response to this pump.
|
|
std::string getName() const { return mPump.getName(); }
|
|
/// Less typically, we'd request the pump itself for some reason.
|
|
LLEventPump& getPump() { return mPump; }
|
|
|
|
/**
|
|
* 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)
|
|
{
|
|
return waitForEventOn(self, mPump);
|
|
}
|
|
|
|
template <typename SELF>
|
|
LLSD postAndWait(SELF& self, const LLSD& event, const LLEventPumpOrPumpName& requestPump,
|
|
const LLSD& replyPumpNamePath=LLSD())
|
|
{
|
|
return ::postAndWait(self, event, requestPump, mPump, replyPumpNamePath);
|
|
}
|
|
|
|
private:
|
|
LLEventStream mPump;
|
|
};
|
|
|
|
/**
|
|
* Other event APIs require the names of two different LLEventPumps: one for
|
|
* success response, the other for error response. Extend LLCoroEventPump
|
|
* for the two-pump use case.
|
|
*/
|
|
class LL_COMMON_API LLCoroEventPumps
|
|
{
|
|
public:
|
|
LLCoroEventPumps(const std::string& name="coro",
|
|
const std::string& suff0="Reply",
|
|
const std::string& suff1="Error"):
|
|
mPump0(name + suff0, true), // allow tweaking the pump instance name
|
|
mPump1(name + suff1, true)
|
|
{}
|
|
/// request pump 0's name
|
|
std::string getName0() const { return mPump0.getName(); }
|
|
/// request pump 1's name
|
|
std::string getName1() const { return mPump1.getName(); }
|
|
/// request both names
|
|
std::pair<std::string, std::string> getNames() const
|
|
{
|
|
return std::pair<std::string, std::string>(mPump0.getName(), mPump1.getName());
|
|
}
|
|
|
|
/// request pump 0
|
|
LLEventPump& getPump0() { return mPump0; }
|
|
/// request pump 1
|
|
LLEventPump& getPump1() { return mPump1; }
|
|
|
|
/// waitForEventOn(self, either of our two LLEventPumps)
|
|
template <typename SELF>
|
|
LLEventWithID wait(SELF& self)
|
|
{
|
|
return waitForEventOn(self, mPump0, mPump1);
|
|
}
|
|
|
|
/// errorException(wait(self))
|
|
template <typename SELF>
|
|
LLSD waitWithException(SELF& self)
|
|
{
|
|
return errorException(wait(self), std::string("Error event on ") + getName1());
|
|
}
|
|
|
|
/// errorLog(wait(self))
|
|
template <typename SELF>
|
|
LLSD waitWithLog(SELF& self)
|
|
{
|
|
return errorLog(wait(self), std::string("Error event on ") + getName1());
|
|
}
|
|
|
|
template <typename SELF>
|
|
LLEventWithID postAndWait(SELF& self, const LLSD& event,
|
|
const LLEventPumpOrPumpName& requestPump,
|
|
const LLSD& replyPump0NamePath=LLSD(),
|
|
const LLSD& replyPump1NamePath=LLSD())
|
|
{
|
|
return postAndWait2(self, event, requestPump, mPump0, mPump1,
|
|
replyPump0NamePath, replyPump1NamePath);
|
|
}
|
|
|
|
template <typename SELF>
|
|
LLSD postAndWaitWithException(SELF& self, const LLSD& event,
|
|
const LLEventPumpOrPumpName& requestPump,
|
|
const LLSD& replyPump0NamePath=LLSD(),
|
|
const LLSD& replyPump1NamePath=LLSD())
|
|
{
|
|
return errorException(postAndWait(self, event, requestPump,
|
|
replyPump0NamePath, replyPump1NamePath),
|
|
std::string("Error event on ") + getName1());
|
|
}
|
|
|
|
template <typename SELF>
|
|
LLSD postAndWaitWithLog(SELF& self, const LLSD& event,
|
|
const LLEventPumpOrPumpName& requestPump,
|
|
const LLSD& replyPump0NamePath=LLSD(),
|
|
const LLSD& replyPump1NamePath=LLSD())
|
|
{
|
|
return errorLog(postAndWait(self, event, requestPump,
|
|
replyPump0NamePath, replyPump1NamePath),
|
|
std::string("Error event on ") + getName1());
|
|
}
|
|
|
|
private:
|
|
LLEventStream mPump0, mPump1;
|
|
};
|
|
|
|
#endif /* ! defined(LL_LLEVENTCORO_H) */
|