llcoros.h 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221
  1. /**
  2. * @file llcoros.h
  3. * @author Nat Goodspeed
  4. * @date 2009-06-02
  5. * @brief Manage running boost::fiber instances
  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_LLCOROS_H
  35. #define LL_LLCOROS_H
  36. #include <string>
  37. #include "boost/fiber/fiber.hpp"
  38. #include "boost/fiber/fss.hpp"
  39. #include "boost/fiber/future/promise.hpp"
  40. #include "boost/fiber/future/future.hpp"
  41. #include "boost/function.hpp"
  42. #include "llerror.h"
  43. #include "llinstancetracker.h"
  44. // Registry of named Boost.Coroutine instances
  45. //
  46. // The viewer's use of the term "coroutine" became deeply embedded before the
  47. // industry term "fiber" emerged to distinguish userland threads from simpler,
  48. // more transient kinds of coroutines. Semantically they have always been
  49. // fibers. But at this point in history, we are pretty much stuck with the term
  50. // "coroutine."
  51. //
  52. // The Boost.Coroutine library supports the general case of a coroutine
  53. // accepting arbitrary parameters and yielding multiple (sets of) results. For
  54. // such use cases, it's natural for the invoking code to retain the coroutine
  55. // instance: the consumer repeatedly calls into the coroutine, perhaps passing
  56. // new parameter values, prompting it to yield its next result.
  57. //
  58. // Our typical coroutine usage is different, though. For us, coroutines
  59. // provide an alternative to the Responder pattern. Our typical coroutine
  60. // has void return, invoked in fire-and-forget mode: the handler for some
  61. // user gesture launches the coroutine and promptly returns to the main loop.
  62. // The coroutine initiates some action that will take multiple frames (e.g. a
  63. // capability request), waits for its result, processes it and silently steals
  64. // away.
  65. //
  66. // This usage poses two (related) problems:
  67. //
  68. // # Who should own the coroutine instance? If it's simply local to the
  69. // handler code that launches it, return from the handler will destroy the
  70. // coroutine object, terminating the coroutine.
  71. // # Once the coroutine terminates, in whatever way, who's responsible for
  72. // cleaning up the coroutine object?
  73. //
  74. // LLCoros is a Singleton collection of currently-active coroutine instances.
  75. // Each has a name. You ask LLCoros to launch a new coroutine with a suggested
  76. // name prefix; from your prefix it generates a distinct name, registers the
  77. // new coroutine and returns the actual name.
  78. //
  79. // The name can be used to kill off the coroutine prematurely, if needed. It
  80. // can also provide diagnostic info: we can look up the name of the
  81. // currently-running coroutine.
  82. //
  83. // Finally, the next frame ("mainloop" event) after the coroutine terminates,
  84. // LLCoros will notice its demise and destroy it.
  85. class LLCoros final
  86. {
  87. protected:
  88. LOG_CLASS(LLCoros);
  89. public:
  90. LLCoros();
  91. ~LLCoros();
  92. // Canonical signature we use
  93. typedef boost::fibers::fiber coro;
  94. // Canonical callable type
  95. typedef boost::function<void()> callable_t;
  96. // Creates and starts running a new coroutine with specified name. The name
  97. // string you pass is a suggestion; it will be tweaked for uniqueness. The
  98. // actual name is returned to you.
  99. //
  100. // Usage looks like this, for (e.g.) two coroutine parameters:
  101. // class MyClass
  102. // {
  103. // public:
  104. // ...
  105. // // Do NOT NOT NOT accept reference params !
  106. // // Pass by value only !
  107. // void myCoroutineMethod(std::string, LLSD);
  108. // ...
  109. // };
  110. // ...
  111. // std::string name =
  112. // gCoros.launch("mycoro",
  113. // boost::bind(&MyClass::myCoroutineMethod, this,
  114. // "somestring", LLSD(17)));
  115. //
  116. // Your function/method can accept any parameters you want -- but ONLY BY
  117. // VALUE ! Reference parameters are a BAD IDEA ! You have been warned. See
  118. // DEV-32777 comments for an explanation.
  119. //
  120. // Pass a nullary callable. It works to directly pass a nullary free
  121. // function (or static method); for all other cases use boost::bind(). Of
  122. // course, for a non-static class method, the first parameter must be the
  123. // class instance. Any other parameters should be passed via the bind()
  124. // expression.
  125. //
  126. // launch() tweaks the suggested name so it won't collide with any
  127. // existing coroutine instance, creates the coroutine instance, registers
  128. // it with the tweaked name and runs it until its first wait. At that
  129. // point it returns the tweaked name.
  130. std::string launch(const std::string& prefix, const callable_t& callable);
  131. // From within a coroutine, looks up the (tweaked) name string by which
  132. // this coroutine is registered. Returns an auto-generated name if not
  133. // found (e.g. if the coroutine was launched by hand rather than using
  134. // LLCoros::launch()).
  135. static std::string getName();
  136. // For delayed initialization
  137. void setStackSize(S32 stacksize);
  138. LL_INLINE bool hasActiveCoroutines() const { return CoroData::instanceCount() > 0; }
  139. void printActiveCoroutines();
  140. // Gets the current coro::id for those who really really care
  141. static coro::id get_self();
  142. // Most coroutines, most of the time, do not "consume" the events for which
  143. // they are suspending. This way, an arbitrary number of listeners (whether
  144. // coroutines or simple callbacks) can be registered on a particular
  145. // LLEventPump, every listener responding to each of the events on that
  146. // LLEventPump. But a particular coroutine can assert that it will consume
  147. // each event for which it suspends. (See also llcoro::postAndSuspend(),
  148. // llcoro::VoidListener)
  149. static void set_consuming(bool consuming);
  150. static bool get_consuming();
  151. // Aliases for promise and future.
  152. // An older underlying future implementation required us to wrap future;
  153. // this is no longer needed. However, if it is important to restore kill()
  154. // functionality, we might need to provide a proxy, so continue using the
  155. // aliases.
  156. template <typename T>
  157. using Promise = boost::fibers::promise<T>;
  158. template <typename T>
  159. using Future = boost::fibers::future<T>;
  160. template <typename T>
  161. static Future<T> getFuture(Promise<T>& promise)
  162. {
  163. return promise.get_future();
  164. }
  165. // For data local to each running coroutine
  166. template <typename T>
  167. using local_ptr = boost::fibers::fiber_specific_ptr<T>;
  168. private:
  169. std::string generateDistinctName(const std::string& prefix) const;
  170. void toplevel(std::string name, callable_t callable);
  171. class CoroData;
  172. static CoroData& getCoroData();
  173. private:
  174. S32 mStackSize;
  175. // Coroutine-local storage, as it were: one per coro we track
  176. class CoroData : public LLInstanceTracker<CoroData, std::string>
  177. {
  178. protected:
  179. LOG_CLASS(LLCoros::CoroData);
  180. public:
  181. CoroData(const std::string& name);
  182. public:
  183. // Tweaked name of the current coroutine
  184. F64 mCreationTime;
  185. std::string mName;
  186. bool mConsuming; // set_consuming() state
  187. };
  188. // Identify the current fiber CoroData
  189. local_ptr<CoroData> mCurrent;
  190. };
  191. extern LLCoros gCoros;
  192. #endif // LL_LLCOROS_H