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