Audio plugin host https://kx.studio/carla
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

329 lines
9.4KB

  1. //
  2. // coroutine.hpp
  3. // ~~~~~~~~~~~~~
  4. //
  5. // Copyright (c) 2003-2015 Christopher M. Kohlhoff (chris at kohlhoff dot com)
  6. //
  7. // Distributed under the Boost Software License, Version 1.0. (See accompanying
  8. // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
  9. //
  10. #ifndef ASIO_COROUTINE_HPP
  11. #define ASIO_COROUTINE_HPP
  12. namespace asio {
  13. namespace detail {
  14. class coroutine_ref;
  15. } // namespace detail
  16. /// Provides support for implementing stackless coroutines.
  17. /**
  18. * The @c coroutine class may be used to implement stackless coroutines. The
  19. * class itself is used to store the current state of the coroutine.
  20. *
  21. * Coroutines are copy-constructible and assignable, and the space overhead is
  22. * a single int. They can be used as a base class:
  23. *
  24. * @code class session : coroutine
  25. * {
  26. * ...
  27. * }; @endcode
  28. *
  29. * or as a data member:
  30. *
  31. * @code class session
  32. * {
  33. * ...
  34. * coroutine coro_;
  35. * }; @endcode
  36. *
  37. * or even bound in as a function argument using lambdas or @c bind(). The
  38. * important thing is that as the application maintains a copy of the object
  39. * for as long as the coroutine must be kept alive.
  40. *
  41. * @par Pseudo-keywords
  42. *
  43. * A coroutine is used in conjunction with certain "pseudo-keywords", which
  44. * are implemented as macros. These macros are defined by a header file:
  45. *
  46. * @code #include <asio/yield.hpp>@endcode
  47. *
  48. * and may conversely be undefined as follows:
  49. *
  50. * @code #include <asio/unyield.hpp>@endcode
  51. *
  52. * <b>reenter</b>
  53. *
  54. * The @c reenter macro is used to define the body of a coroutine. It takes a
  55. * single argument: a pointer or reference to a coroutine object. For example,
  56. * if the base class is a coroutine object you may write:
  57. *
  58. * @code reenter (this)
  59. * {
  60. * ... coroutine body ...
  61. * } @endcode
  62. *
  63. * and if a data member or other variable you can write:
  64. *
  65. * @code reenter (coro_)
  66. * {
  67. * ... coroutine body ...
  68. * } @endcode
  69. *
  70. * When @c reenter is executed at runtime, control jumps to the location of the
  71. * last @c yield or @c fork.
  72. *
  73. * The coroutine body may also be a single statement, such as:
  74. *
  75. * @code reenter (this) for (;;)
  76. * {
  77. * ...
  78. * } @endcode
  79. *
  80. * @b Limitation: The @c reenter macro is implemented using a switch. This
  81. * means that you must take care when using local variables within the
  82. * coroutine body. The local variable is not allowed in a position where
  83. * reentering the coroutine could bypass the variable definition.
  84. *
  85. * <b>yield <em>statement</em></b>
  86. *
  87. * This form of the @c yield keyword is often used with asynchronous operations:
  88. *
  89. * @code yield socket_->async_read_some(buffer(*buffer_), *this); @endcode
  90. *
  91. * This divides into four logical steps:
  92. *
  93. * @li @c yield saves the current state of the coroutine.
  94. * @li The statement initiates the asynchronous operation.
  95. * @li The resume point is defined immediately following the statement.
  96. * @li Control is transferred to the end of the coroutine body.
  97. *
  98. * When the asynchronous operation completes, the function object is invoked
  99. * and @c reenter causes control to transfer to the resume point. It is
  100. * important to remember to carry the coroutine state forward with the
  101. * asynchronous operation. In the above snippet, the current class is a
  102. * function object object with a coroutine object as base class or data member.
  103. *
  104. * The statement may also be a compound statement, and this permits us to
  105. * define local variables with limited scope:
  106. *
  107. * @code yield
  108. * {
  109. * mutable_buffers_1 b = buffer(*buffer_);
  110. * socket_->async_read_some(b, *this);
  111. * } @endcode
  112. *
  113. * <b>yield return <em>expression</em> ;</b>
  114. *
  115. * This form of @c yield is often used in generators or coroutine-based parsers.
  116. * For example, the function object:
  117. *
  118. * @code struct interleave : coroutine
  119. * {
  120. * istream& is1;
  121. * istream& is2;
  122. * char operator()(char c)
  123. * {
  124. * reenter (this) for (;;)
  125. * {
  126. * yield return is1.get();
  127. * yield return is2.get();
  128. * }
  129. * }
  130. * }; @endcode
  131. *
  132. * defines a trivial coroutine that interleaves the characters from two input
  133. * streams.
  134. *
  135. * This type of @c yield divides into three logical steps:
  136. *
  137. * @li @c yield saves the current state of the coroutine.
  138. * @li The resume point is defined immediately following the semicolon.
  139. * @li The value of the expression is returned from the function.
  140. *
  141. * <b>yield ;</b>
  142. *
  143. * This form of @c yield is equivalent to the following steps:
  144. *
  145. * @li @c yield saves the current state of the coroutine.
  146. * @li The resume point is defined immediately following the semicolon.
  147. * @li Control is transferred to the end of the coroutine body.
  148. *
  149. * This form might be applied when coroutines are used for cooperative
  150. * threading and scheduling is explicitly managed. For example:
  151. *
  152. * @code struct task : coroutine
  153. * {
  154. * ...
  155. * void operator()()
  156. * {
  157. * reenter (this)
  158. * {
  159. * while (... not finished ...)
  160. * {
  161. * ... do something ...
  162. * yield;
  163. * ... do some more ...
  164. * yield;
  165. * }
  166. * }
  167. * }
  168. * ...
  169. * };
  170. * ...
  171. * task t1, t2;
  172. * for (;;)
  173. * {
  174. * t1();
  175. * t2();
  176. * } @endcode
  177. *
  178. * <b>yield break ;</b>
  179. *
  180. * The final form of @c yield is used to explicitly terminate the coroutine.
  181. * This form is comprised of two steps:
  182. *
  183. * @li @c yield sets the coroutine state to indicate termination.
  184. * @li Control is transferred to the end of the coroutine body.
  185. *
  186. * Once terminated, calls to is_complete() return true and the coroutine cannot
  187. * be reentered.
  188. *
  189. * Note that a coroutine may also be implicitly terminated if the coroutine
  190. * body is exited without a yield, e.g. by return, throw or by running to the
  191. * end of the body.
  192. *
  193. * <b>fork <em>statement</em></b>
  194. *
  195. * The @c fork pseudo-keyword is used when "forking" a coroutine, i.e. splitting
  196. * it into two (or more) copies. One use of @c fork is in a server, where a new
  197. * coroutine is created to handle each client connection:
  198. *
  199. * @code reenter (this)
  200. * {
  201. * do
  202. * {
  203. * socket_.reset(new tcp::socket(io_context_));
  204. * yield acceptor->async_accept(*socket_, *this);
  205. * fork server(*this)();
  206. * } while (is_parent());
  207. * ... client-specific handling follows ...
  208. * } @endcode
  209. *
  210. * The logical steps involved in a @c fork are:
  211. *
  212. * @li @c fork saves the current state of the coroutine.
  213. * @li The statement creates a copy of the coroutine and either executes it
  214. * immediately or schedules it for later execution.
  215. * @li The resume point is defined immediately following the semicolon.
  216. * @li For the "parent", control immediately continues from the next line.
  217. *
  218. * The functions is_parent() and is_child() can be used to differentiate
  219. * between parent and child. You would use these functions to alter subsequent
  220. * control flow.
  221. *
  222. * Note that @c fork doesn't do the actual forking by itself. It is the
  223. * application's responsibility to create a clone of the coroutine and call it.
  224. * The clone can be called immediately, as above, or scheduled for delayed
  225. * execution using something like io_context::post().
  226. *
  227. * @par Alternate macro names
  228. *
  229. * If preferred, an application can use macro names that follow a more typical
  230. * naming convention, rather than the pseudo-keywords. These are:
  231. *
  232. * @li @c ASIO_CORO_REENTER instead of @c reenter
  233. * @li @c ASIO_CORO_YIELD instead of @c yield
  234. * @li @c ASIO_CORO_FORK instead of @c fork
  235. */
  236. class coroutine
  237. {
  238. public:
  239. /// Constructs a coroutine in its initial state.
  240. coroutine() : value_(0) {}
  241. /// Returns true if the coroutine is the child of a fork.
  242. bool is_child() const { return value_ < 0; }
  243. /// Returns true if the coroutine is the parent of a fork.
  244. bool is_parent() const { return !is_child(); }
  245. /// Returns true if the coroutine has reached its terminal state.
  246. bool is_complete() const { return value_ == -1; }
  247. private:
  248. friend class detail::coroutine_ref;
  249. int value_;
  250. };
  251. namespace detail {
  252. class coroutine_ref
  253. {
  254. public:
  255. coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {}
  256. coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {}
  257. ~coroutine_ref() { if (!modified_) value_ = -1; }
  258. operator int() const { return value_; }
  259. int& operator=(int v) { modified_ = true; return value_ = v; }
  260. private:
  261. void operator=(const coroutine_ref&);
  262. int& value_;
  263. bool modified_;
  264. };
  265. } // namespace detail
  266. } // namespace asio
  267. #define ASIO_CORO_REENTER(c) \
  268. switch (::asio::detail::coroutine_ref _coro_value = c) \
  269. case -1: if (_coro_value) \
  270. { \
  271. goto terminate_coroutine; \
  272. terminate_coroutine: \
  273. _coro_value = -1; \
  274. goto bail_out_of_coroutine; \
  275. bail_out_of_coroutine: \
  276. break; \
  277. } \
  278. else case 0:
  279. #define ASIO_CORO_YIELD_IMPL(n) \
  280. for (_coro_value = (n);;) \
  281. if (_coro_value == 0) \
  282. { \
  283. case (n): ; \
  284. break; \
  285. } \
  286. else \
  287. switch (_coro_value ? 0 : 1) \
  288. for (;;) \
  289. case -1: if (_coro_value) \
  290. goto terminate_coroutine; \
  291. else for (;;) \
  292. case 1: if (_coro_value) \
  293. goto bail_out_of_coroutine; \
  294. else case 0:
  295. #define ASIO_CORO_FORK_IMPL(n) \
  296. for (_coro_value = -(n);; _coro_value = (n)) \
  297. if (_coro_value == (n)) \
  298. { \
  299. case -(n): ; \
  300. break; \
  301. } \
  302. else
  303. #if defined(_MSC_VER)
  304. # define ASIO_CORO_YIELD ASIO_CORO_YIELD_IMPL(__COUNTER__ + 1)
  305. # define ASIO_CORO_FORK ASIO_CORO_FORK_IMPL(__COUNTER__ + 1)
  306. #else // defined(_MSC_VER)
  307. # define ASIO_CORO_YIELD ASIO_CORO_YIELD_IMPL(__LINE__)
  308. # define ASIO_CORO_FORK ASIO_CORO_FORK_IMPL(__LINE__)
  309. #endif // defined(_MSC_VER)
  310. #endif // ASIO_COROUTINE_HPP