llcoros.cpp 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252
  1. /**
  2. * @file llcoros.cpp
  3. * @author Nat Goodspeed
  4. * @date 2009-06-03
  5. * @brief Implementation for llcoros.
  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. #include "linden_common.h"
  35. #ifndef BOOST_DISABLE_ASSERTS
  36. # define UNDO_BOOST_DISABLE_ASSERTS
  37. #endif
  38. // With Boost 1.65.1, needed for Mac with this specific header
  39. #ifndef BOOST_DISABLE_ASSERTS
  40. # define BOOST_DISABLE_ASSERTS
  41. #endif
  42. #include "boost/fiber/protected_fixedsize_stack.hpp"
  43. #undef UNDO_BOOST_DISABLE_ASSERTS
  44. #undef BOOST_DISABLE_ASSERTS
  45. #include "boost/fiber/exceptions.hpp"
  46. #include "llcoros.h"
  47. #include "llevents.h"
  48. #include "lltimer.h"
  49. // Global
  50. LLCoros gCoros;
  51. static bool sDestroyed = false;
  52. // Default coroutine stack size.
  53. constexpr S32 LLCOROS_STACKSIZE = 768 * 1024;
  54. LLCoros::LLCoros()
  55. : mStackSize(LLCOROS_STACKSIZE),
  56. // mCurrent does NOT own the current CoroData instance: it simply points to
  57. // it. So initialize it with a no-op deleter.
  58. mCurrent{ [](CoroData*){} }
  59. {
  60. }
  61. LLCoros::~LLCoros()
  62. {
  63. sDestroyed = true;
  64. }
  65. std::string LLCoros::generateDistinctName(const std::string& prefix) const
  66. {
  67. static S32 unique = 0;
  68. // Allowing empty name would make getName()'s not-found return ambiguous.
  69. if (prefix.empty())
  70. {
  71. llerrs << "Empty name string !" << llendl;
  72. }
  73. // If the specified name is not already in the map, just use that.
  74. std::string name = prefix;
  75. // Until we find an unused name, append a numeric suffix for uniqueness.
  76. while (CoroData::getNamedInstance(name))
  77. {
  78. name = llformat("%s%d", prefix.c_str(), unique++);
  79. }
  80. return name;
  81. }
  82. //static
  83. LLCoros::CoroData& LLCoros::getCoroData()
  84. {
  85. CoroData* current = NULL;
  86. if (!sDestroyed)
  87. {
  88. current = gCoros.mCurrent.get();
  89. }
  90. // For the main() coroutine, the one NOT explicitly launched by launch(),
  91. // we never explicitly set mCurrent. Use a static CoroData instance with
  92. // canonical values.
  93. if (!current)
  94. {
  95. // It is tempting to provide a distinct name for each thread's "main
  96. // coroutine." But as getName() has always returned the empty string to
  97. // mean "not in a coroutine," empty string should suffice here.
  98. static thread_local CoroData tMain("");
  99. // We need not reset() the local_ptr to this instance; we will simply
  100. // find it again every time we discover that current is NULL.
  101. current = &tMain;
  102. }
  103. return *current;
  104. }
  105. //static
  106. std::string LLCoros::getName()
  107. {
  108. return getCoroData().mName;
  109. }
  110. //static
  111. LLCoros::coro::id LLCoros::get_self()
  112. {
  113. return boost::this_fiber::get_id();
  114. }
  115. //static
  116. void LLCoros::set_consuming(bool consuming)
  117. {
  118. CoroData& data(getCoroData());
  119. // DO NOT call this on the main() coroutine.
  120. llassert_always(!data.mName.empty());
  121. data.mConsuming = consuming;
  122. }
  123. //static
  124. bool LLCoros::get_consuming()
  125. {
  126. return getCoroData().mConsuming;
  127. }
  128. void LLCoros::setStackSize(S32 stacksize)
  129. {
  130. llinfos << "Setting coroutine stack size to " << stacksize << llendl;
  131. mStackSize = stacksize;
  132. }
  133. // Top-level wrapper around caller's coroutine callable.
  134. // Normally we like to pass strings and such by const reference, but in this
  135. // case, we WANT to copy both the name and the callable to our local stack !
  136. void LLCoros::toplevel(std::string name, callable_t callable)
  137. {
  138. // Keep the CoroData on this top-level function's stack frame
  139. CoroData corodata(name);
  140. // Set it as current
  141. mCurrent.reset(&corodata);
  142. // Run the code the caller actually wants in the coroutine
  143. try
  144. {
  145. // Run the code the caller actually wants in the coroutine
  146. callable();
  147. }
  148. catch (std::exception& e)
  149. {
  150. llwarns << "Caught exception '" << e.what() << "' during coroutine"
  151. << llendl;
  152. }
  153. catch (...)
  154. {
  155. llwarns << "An unknown exception occurred during coroutine" << llendl;
  156. }
  157. }
  158. std::string LLCoros::launch(const std::string& prefix,
  159. const callable_t& callable)
  160. {
  161. std::string name = generateDistinctName(prefix);
  162. LL_DEBUGS("Coros") << "Launching coroutine: " << name << LL_ENDL;
  163. try
  164. {
  165. // 'dispatch' means: enter the new fiber immediately, returning here
  166. // only when the fiber yields for whatever reason. std::allocator_arg
  167. // is a flag to indicate that the following argument is a
  168. // StackAllocator. protected_fixedsize_stack sets a guard page past the
  169. // end of the new stack so that stack underflow will result in an
  170. // access violation instead of weird, subtle, possibly undiagnosed
  171. // memory stomps.
  172. boost::fibers::fiber new_coro(boost::fibers::launch::dispatch,
  173. std::allocator_arg,
  174. boost::fibers::protected_fixedsize_stack(mStackSize),
  175. [this, &name, &callable]()
  176. {
  177. toplevel(name, callable);
  178. });
  179. // You have two choices with a fiber instance: you can join() it or you
  180. // can detach() it. If you try to destroy the instance before doing
  181. // either, the program silently terminates.
  182. new_coro.detach();
  183. }
  184. catch (...)
  185. {
  186. llwarns << "Failed to start coroutine: " << name << llendl;
  187. }
  188. return name;
  189. }
  190. void LLCoros::printActiveCoroutines()
  191. {
  192. if (!CoroData::instanceCount())
  193. {
  194. llinfos << "No active coroutine" << llendl;
  195. return;
  196. }
  197. llwarns << "------ List of active coroutines ------\n";
  198. F64 now = LLTimer::getTotalSeconds();
  199. for (auto& cd : CoroData::instance_snapshot())
  200. {
  201. F64 life_time = now - cd.mCreationTime;
  202. llcont << "Name: " << cd.mName << " - Key: " << cd.getKey()
  203. << " - Life time: " << life_time << " seconds.\n";
  204. }
  205. llcont << "---------------------------------------" << llendl;
  206. }
  207. ///////////////////////////////////////////////////////////////////////////////
  208. // LLCoros::CoroData sub-class
  209. ///////////////////////////////////////////////////////////////////////////////
  210. LLCoros::CoroData::CoroData(const std::string& name)
  211. : LLInstanceTracker<CoroData, std::string>(name),
  212. mName(name),
  213. // Do not consume events unless specifically directed
  214. mConsuming(false),
  215. mCreationTime(LLTimer::getTotalSeconds())
  216. {
  217. if (name.empty())
  218. {
  219. mName = LLEventPump::inventName("coro");
  220. llinfos << "Auto-generated coro name: " << mName << llendl;
  221. }
  222. LL_DEBUGS("Coros") << "Created CoroData for coroutine: " << mName
  223. << LL_ENDL;
  224. }