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