lleventcoro.h 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219
  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, 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. #ifndef LL_LLEVENTCORO_H
  35. #define LL_LLEVENTCORO_H
  36. #include <string>
  37. #include "boost/optional.hpp"
  38. #include "llerror.h"
  39. #include "llevents.h"
  40. // Like LLListenerOrPumpName, this is a class intended for parameter lists:
  41. // accepts a const LLEventPumpOrPumpName& and you can accept either an
  42. // LLEventPump& or its string name. For a single parameter that could be
  43. // either, it is not hard to overload the function, but as soon as you want to
  44. // accept two such parameters, this is cheaper than four overloads.
  45. class LLEventPumpOrPumpName
  46. {
  47. public:
  48. // Pass an actual LLEventPump&
  49. LLEventPumpOrPumpName(LLEventPump& pump)
  50. : mPump(pump)
  51. {
  52. }
  53. // Pass the string name of an LLEventPump
  54. LLEventPumpOrPumpName(const std::string& pumpname)
  55. : mPump(gEventPumps.obtain(pumpname))
  56. {
  57. }
  58. // Pass string constant name of an LLEventPump. This override must be
  59. // explicit, since otherwise passing const char* to a function
  60. // accepting const LLEventPumpOrPumpName& would require two
  61. // different implicit conversions: const char* -> const
  62. // std::string& -> const LLEventPumpOrPumpName&.
  63. LLEventPumpOrPumpName(const char* pumpname)
  64. : mPump(gEventPumps.obtain(pumpname))
  65. {
  66. }
  67. // Unspecified: "I choose not to identify an LLEventPump."
  68. LLEventPumpOrPumpName() = default;
  69. LL_INLINE operator LLEventPump&() const { return *mPump; }
  70. LL_INLINE LLEventPump& getPump() const { return *mPump; }
  71. LL_INLINE operator bool() const { return bool(mPump); }
  72. LL_INLINE bool operator!() const { return !mPump; }
  73. private:
  74. boost::optional<LLEventPump&> mPump;
  75. };
  76. namespace llcoro
  77. {
  78. // Yields control from a coroutine for one "mainloop" tick. If your coroutine
  79. // runs without suspending for nontrivial time, sprinkle in calls to this
  80. // function to avoid stalling the rest of the viewer processing.
  81. void suspend();
  82. // Yields control from a coroutine for at least the specified number of seconds
  83. void suspendUntilTimeout(F32 seconds);
  84. // Posts specified LLSD event on the specified LLEventPump, then suspend for a
  85. // response on specified other LLEventPump. This is more than mere convenience:
  86. // the difference between this function and the sequence.
  87. // Example code:
  88. // request_pump.post(myEvent);
  89. // LLSD reply = suspendUntilEventOn(reply_pump);
  90. // is that the sequence above fails if the reply is posted immediately on
  91. // reply_pump, that is, before request_pump.post() returns. In the sequence
  92. // above, the running coroutine is not even listening on reply_pump until
  93. // request_pump.post() returns and suspendUntilEventOn() is entered. Therefore,
  94. // the coroutine completely misses an immediate reply event, making it suspend
  95. // indefinitely.
  96. // By contrast, postAndSuspend() listens on the reply_pump before posting the
  97. // specified LLSD event on the specified request_pump.
  98. // 'event' is the LLSD data to be posted on request_pump.
  99. // 'request_pump' is an LLEventPump on which to post 'event'. Pass either the
  100. // LLEventPump& or its string name. However, if you pass a default-constructed
  101. // LLEventPumpOrPumpName, we skip the post() call.
  102. // 'reply_pump' is an LLEventPump on which postAndSuspend() will listen for a
  103. // reply. Pass either the LLEventPump& or its string name. The calling
  104. // coroutine will suspend until that reply arrives (if you are concerned about
  105. // a reply that might not arrive, please see also LLEventTimeout).
  106. // 'reply_pump_name_path' specifies the location within event in which to
  107. // store reply_pump.getName(). This is a strictly optional convenience feature;
  108. // obviously you can store the name in event "by hand" if desired.
  109. // 'reply_pump_name_path' can be specified in any of four forms:
  110. // * isUndefined() (default-constructed LLSD object): do nothing. This is the
  111. // default behavior if you omit reply_pump_name_path.
  112. // * isInteger(): event is an array. Store reply_pump.getName() in
  113. // event[reply_pump_name_path.asInteger()].
  114. // * isString(): event is a map. Store reply_pump.getName() in
  115. // event[reply_pump_name_path.asString()].
  116. // * isArray(): event has several levels of structure, e.g. map of maps, array
  117. // of arrays, array of maps, map of arrays, ... Store reply_pump.getName() in
  118. // event[reply_pump_name_path[0]][reply_pump_name_path[1]]... In other words,
  119. // examine each array entry in reply_pump_name_path in turn. If it is an
  120. // LLSD::String, the current level of event is a map; step down to that map
  121. // entry. If it is an LLSD::Integer, the current level of event is an array;
  122. // step down to that array entry. The last array entry in
  123. // reply_pump_name_path specifies the entry in the lowest-level structure in
  124. // event into which to store reply_pump.getName().
  125. LLSD postAndSuspend(const LLSD& event,
  126. const LLEventPumpOrPumpName& request_pump,
  127. const LLEventPumpOrPumpName& reply_pump,
  128. const LLSD& reply_pump_name_path = "reply");
  129. // Wait for the next event on the specified LLEventPump. Pass either the
  130. // LLEventPump& or its string name.
  131. LL_INLINE LLSD suspendUntilEventOn(const LLEventPumpOrPumpName& pump)
  132. {
  133. // This is now a convenience wrapper for postAndSuspend().
  134. return postAndSuspend(LLSD(), LLEventPumpOrPumpName(), pump);
  135. }
  136. } // namespace llcoro
  137. // Certain event APIs require the name of an LLEventPump on which they should
  138. // post results. While it works to invent a distinct name and let
  139. // LLEventPumps::obtain() instantiate the LLEventPump as a "named singleton,"
  140. // in a certain sense it is more robust to instantiate a local LLEventPump and
  141. // provide its name instead. This class packages the following idiom:
  142. // 1. Instantiate a local LLCoroEventPump, with an optional name prefix.
  143. // 2. Provide its actual name to the event API in question as the name of the
  144. // reply LLEventPump.
  145. // 3. Initiate the request to the event API.
  146. // 4. Call your LLEventTempStream's suspend() method to suspend for the reply.
  147. // 5. Let the LLCoroEventPump go out of scope.
  148. class LLCoroEventPump
  149. {
  150. protected:
  151. LOG_CLASS(LLCoroEventPump);
  152. public:
  153. LLCoroEventPump(const std::string& name = "coro")
  154. : mPump(name, true) // allow tweaking the pump instance name
  155. {
  156. }
  157. // It is typical to request the LLEventPump name to direct an event API to
  158. // send its response to this pump.
  159. LL_INLINE std::string getName() const { return mPump.getName(); }
  160. // Less typically, we would request the pump itself for some reason.
  161. LL_INLINE LLEventPump& getPump() { return mPump; }
  162. // Wait for an event on this LLEventPump.
  163. LL_INLINE LLSD suspend()
  164. {
  165. return llcoro::suspendUntilEventOn(mPump);
  166. }
  167. LL_INLINE LLSD postAndSuspend(const LLSD& event,
  168. const LLEventPumpOrPumpName& request_pump,
  169. const LLSD& reply_pump_name_path = LLSD())
  170. {
  171. return llcoro::postAndSuspend(event, request_pump, mPump,
  172. reply_pump_name_path);
  173. }
  174. private:
  175. LLEventStream mPump;
  176. };
  177. namespace llcoro
  178. {
  179. // Instantiates a temporary local LLCoroEventPump and call its postAndSuspend()
  180. // method, returning the result. This supports a one-liner query of some
  181. // LLEventAPI. For multiple calls from the same function, it is still cheaper
  182. // to instantiate LLCoroEventPump explicitly.
  183. LL_INLINE LLSD postAndSuspendTemp(const LLSD& event,
  184. const LLEventPumpOrPumpName& req_pump,
  185. const LLSD& reply_pump_name = "reply")
  186. {
  187. LLCoroEventPump waiter;
  188. return waiter.postAndSuspend(event, req_pump, reply_pump_name);
  189. }
  190. } // namespace llcoro
  191. #endif // LL_LLEVENTCORO_H