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