/indra/llcommon/llcoros.h

https://bitbucket.org/lindenlab/viewer-beta/ · C++ Header · 166 lines · 37 code · 10 blank · 119 comment · 0 complexity · 0bc52c798fef9b5488c16c42801f64b1 MD5 · raw file

  1. /**
  2. * @file llcoros.h
  3. * @author Nat Goodspeed
  4. * @date 2009-06-02
  5. * @brief Manage running boost::coroutine instances
  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_LLCOROS_H)
  29. #define LL_LLCOROS_H
  30. #include <boost/coroutine/coroutine.hpp>
  31. #include "llsingleton.h"
  32. #include <boost/ptr_container/ptr_map.hpp>
  33. #include <string>
  34. #include <boost/preprocessor/repetition/enum_params.hpp>
  35. #include <boost/preprocessor/repetition/enum_binary_params.hpp>
  36. #include <boost/preprocessor/iteration/local.hpp>
  37. #include <stdexcept>
  38. /**
  39. * Registry of named Boost.Coroutine instances
  40. *
  41. * The Boost.Coroutine library supports the general case of a coroutine
  42. * accepting arbitrary parameters and yielding multiple (sets of) results. For
  43. * such use cases, it's natural for the invoking code to retain the coroutine
  44. * instance: the consumer repeatedly calls into the coroutine, perhaps passing
  45. * new parameter values, prompting it to yield its next result.
  46. *
  47. * Our typical coroutine usage is different, though. For us, coroutines
  48. * provide an alternative to the @c Responder pattern. Our typical coroutine
  49. * has @c void return, invoked in fire-and-forget mode: the handler for some
  50. * user gesture launches the coroutine and promptly returns to the main loop.
  51. * The coroutine initiates some action that will take multiple frames (e.g. a
  52. * capability request), waits for its result, processes it and silently steals
  53. * away.
  54. *
  55. * This usage poses two (related) problems:
  56. *
  57. * # Who should own the coroutine instance? If it's simply local to the
  58. * handler code that launches it, return from the handler will destroy the
  59. * coroutine object, terminating the coroutine.
  60. * # Once the coroutine terminates, in whatever way, who's responsible for
  61. * cleaning up the coroutine object?
  62. *
  63. * LLCoros is a Singleton collection of currently-active coroutine instances.
  64. * Each has a name. You ask LLCoros to launch a new coroutine with a suggested
  65. * name prefix; from your prefix it generates a distinct name, registers the
  66. * new coroutine and returns the actual name.
  67. *
  68. * The name can be used to kill off the coroutine prematurely, if needed. It
  69. * can also provide diagnostic info: we can look up the name of the
  70. * currently-running coroutine.
  71. *
  72. * Finally, the next frame ("mainloop" event) after the coroutine terminates,
  73. * LLCoros will notice its demise and destroy it.
  74. */
  75. class LL_COMMON_API LLCoros: public LLSingleton<LLCoros>
  76. {
  77. public:
  78. /// Canonical boost::coroutines::coroutine signature we use
  79. typedef boost::coroutines::coroutine<void()> coro;
  80. /// Canonical 'self' type
  81. typedef coro::self self;
  82. /**
  83. * Create and start running a new coroutine with specified name. The name
  84. * string you pass is a suggestion; it will be tweaked for uniqueness. The
  85. * actual name is returned to you.
  86. *
  87. * Usage looks like this, for (e.g.) two coroutine parameters:
  88. * @code
  89. * class MyClass
  90. * {
  91. * public:
  92. * ...
  93. * // Do NOT NOT NOT accept reference params other than 'self'!
  94. * // Pass by value only!
  95. * void myCoroutineMethod(LLCoros::self& self, std::string, LLSD);
  96. * ...
  97. * };
  98. * ...
  99. * std::string name = LLCoros::instance().launch(
  100. * "mycoro", boost::bind(&MyClass::myCoroutineMethod, this, _1,
  101. * "somestring", LLSD(17));
  102. * @endcode
  103. *
  104. * Your function/method must accept LLCoros::self& as its first parameter.
  105. * It can accept any other parameters you want -- but ONLY BY VALUE!
  106. * Other reference parameters are a BAD IDEA! You Have Been Warned. See
  107. * DEV-32777 comments for an explanation.
  108. *
  109. * Pass a callable that accepts the single LLCoros::self& parameter. It
  110. * may work to pass a free function whose only parameter is 'self'; for
  111. * all other cases use boost::bind(). Of course, for a non-static class
  112. * method, the first parameter must be the class instance. Use the
  113. * placeholder _1 for the 'self' parameter. Any other parameters should be
  114. * passed via the bind() expression.
  115. *
  116. * launch() tweaks the suggested name so it won't collide with any
  117. * existing coroutine instance, creates the coroutine instance, registers
  118. * it with the tweaked name and runs it until its first wait. At that
  119. * point it returns the tweaked name.
  120. */
  121. template <typename CALLABLE>
  122. std::string launch(const std::string& prefix, const CALLABLE& callable)
  123. {
  124. return launchImpl(prefix, new coro(callable));
  125. }
  126. /**
  127. * Abort a running coroutine by name. Normally, when a coroutine either
  128. * runs to completion or terminates with an exception, LLCoros quietly
  129. * cleans it up. This is for use only when you must explicitly interrupt
  130. * one prematurely. Returns @c true if the specified name was found and
  131. * still running at the time.
  132. */
  133. bool kill(const std::string& name);
  134. /**
  135. * From within a coroutine, pass its @c self object to look up the
  136. * (tweaked) name string by which this coroutine is registered. Returns
  137. * the empty string if not found (e.g. if the coroutine was launched by
  138. * hand rather than using LLCoros::launch()).
  139. */
  140. template <typename COROUTINE_SELF>
  141. std::string getName(const COROUTINE_SELF& self) const
  142. {
  143. return getNameByID(self.get_id());
  144. }
  145. /// getName() by self.get_id()
  146. std::string getNameByID(const void* self_id) const;
  147. private:
  148. friend class LLSingleton<LLCoros>;
  149. LLCoros();
  150. std::string launchImpl(const std::string& prefix, coro* newCoro);
  151. std::string generateDistinctName(const std::string& prefix) const;
  152. bool cleanup(const LLSD&);
  153. typedef boost::ptr_map<std::string, coro> CoroMap;
  154. CoroMap mCoros;
  155. };
  156. #endif /* ! defined(LL_LLCOROS_H) */