PageRenderTime 401ms CodeModel.GetById 176ms RepoModel.GetById 2ms app.codeStats 0ms

/indra/llcommon/lleventcoro.h

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