]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // spawn.hpp | |
3 | // ~~~~~~~~~ | |
4 | // | |
92f5a8d4 | 5 | // Copyright (c) 2003-2019 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_SPAWN_HPP | |
12 | #define BOOST_ASIO_SPAWN_HPP | |
13 | ||
14 | #if defined(_MSC_VER) && (_MSC_VER >= 1200) | |
15 | # pragma once | |
16 | #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) | |
17 | ||
18 | #include <boost/asio/detail/config.hpp> | |
19 | #include <boost/coroutine/all.hpp> | |
b32b8144 FG |
20 | #include <boost/asio/bind_executor.hpp> |
21 | #include <boost/asio/detail/memory.hpp> | |
22 | #include <boost/asio/detail/type_traits.hpp> | |
7c673cae | 23 | #include <boost/asio/detail/wrapped_handler.hpp> |
b32b8144 FG |
24 | #include <boost/asio/executor.hpp> |
25 | #include <boost/asio/io_context.hpp> | |
26 | #include <boost/asio/is_executor.hpp> | |
7c673cae FG |
27 | #include <boost/asio/strand.hpp> |
28 | ||
29 | #include <boost/asio/detail/push_options.hpp> | |
30 | ||
31 | namespace boost { | |
32 | namespace asio { | |
33 | ||
34 | /// Context object the represents the currently executing coroutine. | |
35 | /** | |
36 | * The basic_yield_context class is used to represent the currently executing | |
37 | * stackful coroutine. A basic_yield_context may be passed as a handler to an | |
38 | * asynchronous operation. For example: | |
39 | * | |
40 | * @code template <typename Handler> | |
41 | * void my_coroutine(basic_yield_context<Handler> yield) | |
42 | * { | |
43 | * ... | |
44 | * std::size_t n = my_socket.async_read_some(buffer, yield); | |
45 | * ... | |
46 | * } @endcode | |
47 | * | |
48 | * The initiating function (async_read_some in the above example) suspends the | |
49 | * current coroutine. The coroutine is resumed when the asynchronous operation | |
50 | * completes, and the result of the operation is returned. | |
51 | */ | |
52 | template <typename Handler> | |
53 | class basic_yield_context | |
54 | { | |
55 | public: | |
56 | /// The coroutine callee type, used by the implementation. | |
57 | /** | |
58 | * When using Boost.Coroutine v1, this type is: | |
59 | * @code typename coroutine<void()> @endcode | |
60 | * When using Boost.Coroutine v2 (unidirectional coroutines), this type is: | |
61 | * @code push_coroutine<void> @endcode | |
62 | */ | |
63 | #if defined(GENERATING_DOCUMENTATION) | |
64 | typedef implementation_defined callee_type; | |
65 | #elif defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2) | |
66 | typedef boost::coroutines::push_coroutine<void> callee_type; | |
67 | #else | |
68 | typedef boost::coroutines::coroutine<void()> callee_type; | |
69 | #endif | |
70 | ||
71 | /// The coroutine caller type, used by the implementation. | |
72 | /** | |
73 | * When using Boost.Coroutine v1, this type is: | |
74 | * @code typename coroutine<void()>::caller_type @endcode | |
75 | * When using Boost.Coroutine v2 (unidirectional coroutines), this type is: | |
76 | * @code pull_coroutine<void> @endcode | |
77 | */ | |
78 | #if defined(GENERATING_DOCUMENTATION) | |
79 | typedef implementation_defined caller_type; | |
80 | #elif defined(BOOST_COROUTINES_UNIDIRECT) || defined(BOOST_COROUTINES_V2) | |
81 | typedef boost::coroutines::pull_coroutine<void> caller_type; | |
82 | #else | |
83 | typedef boost::coroutines::coroutine<void()>::caller_type caller_type; | |
84 | #endif | |
85 | ||
86 | /// Construct a yield context to represent the specified coroutine. | |
87 | /** | |
88 | * Most applications do not need to use this constructor. Instead, the | |
89 | * spawn() function passes a yield context as an argument to the coroutine | |
90 | * function. | |
91 | */ | |
92 | basic_yield_context( | |
93 | const detail::weak_ptr<callee_type>& coro, | |
94 | caller_type& ca, Handler& handler) | |
95 | : coro_(coro), | |
96 | ca_(ca), | |
97 | handler_(handler), | |
98 | ec_(0) | |
99 | { | |
100 | } | |
101 | ||
b32b8144 FG |
102 | /// Construct a yield context from another yield context type. |
103 | /** | |
104 | * Requires that OtherHandler be convertible to Handler. | |
105 | */ | |
106 | template <typename OtherHandler> | |
107 | basic_yield_context(const basic_yield_context<OtherHandler>& other) | |
108 | : coro_(other.coro_), | |
109 | ca_(other.ca_), | |
110 | handler_(other.handler_), | |
111 | ec_(other.ec_) | |
112 | { | |
113 | } | |
114 | ||
7c673cae FG |
115 | /// Return a yield context that sets the specified error_code. |
116 | /** | |
117 | * By default, when a yield context is used with an asynchronous operation, a | |
118 | * non-success error_code is converted to system_error and thrown. This | |
119 | * operator may be used to specify an error_code object that should instead be | |
120 | * set with the asynchronous operation's result. For example: | |
121 | * | |
122 | * @code template <typename Handler> | |
123 | * void my_coroutine(basic_yield_context<Handler> yield) | |
124 | * { | |
125 | * ... | |
126 | * std::size_t n = my_socket.async_read_some(buffer, yield[ec]); | |
127 | * if (ec) | |
128 | * { | |
129 | * // An error occurred. | |
130 | * } | |
131 | * ... | |
132 | * } @endcode | |
133 | */ | |
134 | basic_yield_context operator[](boost::system::error_code& ec) const | |
135 | { | |
136 | basic_yield_context tmp(*this); | |
137 | tmp.ec_ = &ec; | |
138 | return tmp; | |
139 | } | |
140 | ||
141 | #if defined(GENERATING_DOCUMENTATION) | |
142 | private: | |
143 | #endif // defined(GENERATING_DOCUMENTATION) | |
144 | detail::weak_ptr<callee_type> coro_; | |
145 | caller_type& ca_; | |
b32b8144 | 146 | Handler handler_; |
7c673cae FG |
147 | boost::system::error_code* ec_; |
148 | }; | |
149 | ||
150 | #if defined(GENERATING_DOCUMENTATION) | |
151 | /// Context object that represents the currently executing coroutine. | |
152 | typedef basic_yield_context<unspecified> yield_context; | |
153 | #else // defined(GENERATING_DOCUMENTATION) | |
154 | typedef basic_yield_context< | |
b32b8144 | 155 | executor_binder<void(*)(), executor> > yield_context; |
7c673cae FG |
156 | #endif // defined(GENERATING_DOCUMENTATION) |
157 | ||
158 | /** | |
159 | * @defgroup spawn boost::asio::spawn | |
160 | * | |
161 | * @brief Start a new stackful coroutine. | |
162 | * | |
163 | * The spawn() function is a high-level wrapper over the Boost.Coroutine | |
164 | * library. This function enables programs to implement asynchronous logic in a | |
165 | * synchronous manner, as illustrated by the following example: | |
166 | * | |
167 | * @code boost::asio::spawn(my_strand, do_echo); | |
168 | * | |
169 | * // ... | |
170 | * | |
171 | * void do_echo(boost::asio::yield_context yield) | |
172 | * { | |
173 | * try | |
174 | * { | |
175 | * char data[128]; | |
176 | * for (;;) | |
177 | * { | |
178 | * std::size_t length = | |
179 | * my_socket.async_read_some( | |
180 | * boost::asio::buffer(data), yield); | |
181 | * | |
182 | * boost::asio::async_write(my_socket, | |
183 | * boost::asio::buffer(data, length), yield); | |
184 | * } | |
185 | * } | |
186 | * catch (std::exception& e) | |
187 | * { | |
188 | * // ... | |
189 | * } | |
190 | * } @endcode | |
191 | */ | |
192 | /*@{*/ | |
193 | ||
b32b8144 FG |
194 | /// Start a new stackful coroutine, calling the specified handler when it |
195 | /// completes. | |
196 | /** | |
197 | * This function is used to launch a new coroutine. | |
198 | * | |
199 | * @param function The coroutine function. The function must have the signature: | |
200 | * @code void function(basic_yield_context<Handler> yield); @endcode | |
201 | * | |
202 | * @param attributes Boost.Coroutine attributes used to customise the coroutine. | |
203 | */ | |
204 | template <typename Function> | |
205 | void spawn(BOOST_ASIO_MOVE_ARG(Function) function, | |
206 | const boost::coroutines::attributes& attributes | |
207 | = boost::coroutines::attributes()); | |
208 | ||
7c673cae FG |
209 | /// Start a new stackful coroutine, calling the specified handler when it |
210 | /// completes. | |
211 | /** | |
212 | * This function is used to launch a new coroutine. | |
213 | * | |
214 | * @param handler A handler to be called when the coroutine exits. More | |
215 | * importantly, the handler provides an execution context (via the the handler | |
216 | * invocation hook) for the coroutine. The handler must have the signature: | |
217 | * @code void handler(); @endcode | |
218 | * | |
219 | * @param function The coroutine function. The function must have the signature: | |
220 | * @code void function(basic_yield_context<Handler> yield); @endcode | |
221 | * | |
222 | * @param attributes Boost.Coroutine attributes used to customise the coroutine. | |
223 | */ | |
224 | template <typename Handler, typename Function> | |
225 | void spawn(BOOST_ASIO_MOVE_ARG(Handler) handler, | |
226 | BOOST_ASIO_MOVE_ARG(Function) function, | |
227 | const boost::coroutines::attributes& attributes | |
b32b8144 FG |
228 | = boost::coroutines::attributes(), |
229 | typename enable_if<!is_executor<typename decay<Handler>::type>::value && | |
230 | !is_convertible<Handler&, execution_context&>::value>::type* = 0); | |
7c673cae FG |
231 | |
232 | /// Start a new stackful coroutine, inheriting the execution context of another. | |
233 | /** | |
234 | * This function is used to launch a new coroutine. | |
235 | * | |
236 | * @param ctx Identifies the current coroutine as a parent of the new | |
237 | * coroutine. This specifies that the new coroutine should inherit the | |
238 | * execution context of the parent. For example, if the parent coroutine is | |
239 | * executing in a particular strand, then the new coroutine will execute in the | |
240 | * same strand. | |
241 | * | |
242 | * @param function The coroutine function. The function must have the signature: | |
243 | * @code void function(basic_yield_context<Handler> yield); @endcode | |
244 | * | |
245 | * @param attributes Boost.Coroutine attributes used to customise the coroutine. | |
246 | */ | |
247 | template <typename Handler, typename Function> | |
248 | void spawn(basic_yield_context<Handler> ctx, | |
249 | BOOST_ASIO_MOVE_ARG(Function) function, | |
250 | const boost::coroutines::attributes& attributes | |
251 | = boost::coroutines::attributes()); | |
252 | ||
b32b8144 | 253 | /// Start a new stackful coroutine that executes on a given executor. |
7c673cae FG |
254 | /** |
255 | * This function is used to launch a new coroutine. | |
256 | * | |
b32b8144 FG |
257 | * @param ex Identifies the executor that will run the coroutine. The new |
258 | * coroutine is implicitly given its own strand within this executor. | |
7c673cae FG |
259 | * |
260 | * @param function The coroutine function. The function must have the signature: | |
261 | * @code void function(yield_context yield); @endcode | |
262 | * | |
263 | * @param attributes Boost.Coroutine attributes used to customise the coroutine. | |
264 | */ | |
b32b8144 FG |
265 | template <typename Function, typename Executor> |
266 | void spawn(const Executor& ex, | |
267 | BOOST_ASIO_MOVE_ARG(Function) function, | |
268 | const boost::coroutines::attributes& attributes | |
269 | = boost::coroutines::attributes(), | |
270 | typename enable_if<is_executor<Executor>::value>::type* = 0); | |
271 | ||
272 | /// Start a new stackful coroutine that executes on a given strand. | |
273 | /** | |
274 | * This function is used to launch a new coroutine. | |
275 | * | |
276 | * @param ex Identifies the strand that will run the coroutine. | |
277 | * | |
278 | * @param function The coroutine function. The function must have the signature: | |
279 | * @code void function(yield_context yield); @endcode | |
280 | * | |
281 | * @param attributes Boost.Coroutine attributes used to customise the coroutine. | |
282 | */ | |
283 | template <typename Function, typename Executor> | |
284 | void spawn(const strand<Executor>& ex, | |
7c673cae FG |
285 | BOOST_ASIO_MOVE_ARG(Function) function, |
286 | const boost::coroutines::attributes& attributes | |
287 | = boost::coroutines::attributes()); | |
288 | ||
b32b8144 | 289 | /// Start a new stackful coroutine that executes in the context of a strand. |
7c673cae FG |
290 | /** |
291 | * This function is used to launch a new coroutine. | |
292 | * | |
b32b8144 FG |
293 | * @param s Identifies a strand. By starting multiple coroutines on the same |
294 | * strand, the implementation ensures that none of those coroutines can execute | |
295 | * simultaneously. | |
7c673cae FG |
296 | * |
297 | * @param function The coroutine function. The function must have the signature: | |
298 | * @code void function(yield_context yield); @endcode | |
299 | * | |
300 | * @param attributes Boost.Coroutine attributes used to customise the coroutine. | |
301 | */ | |
302 | template <typename Function> | |
b32b8144 | 303 | void spawn(const boost::asio::io_context::strand& s, |
7c673cae FG |
304 | BOOST_ASIO_MOVE_ARG(Function) function, |
305 | const boost::coroutines::attributes& attributes | |
306 | = boost::coroutines::attributes()); | |
307 | ||
b32b8144 FG |
308 | /// Start a new stackful coroutine that executes on a given execution context. |
309 | /** | |
310 | * This function is used to launch a new coroutine. | |
311 | * | |
312 | * @param ctx Identifies the execution context that will run the coroutine. The | |
313 | * new coroutine is implicitly given its own strand within this execution | |
314 | * context. | |
315 | * | |
316 | * @param function The coroutine function. The function must have the signature: | |
317 | * @code void function(yield_context yield); @endcode | |
318 | * | |
319 | * @param attributes Boost.Coroutine attributes used to customise the coroutine. | |
320 | */ | |
321 | template <typename Function, typename ExecutionContext> | |
322 | void spawn(ExecutionContext& ctx, | |
323 | BOOST_ASIO_MOVE_ARG(Function) function, | |
324 | const boost::coroutines::attributes& attributes | |
325 | = boost::coroutines::attributes(), | |
326 | typename enable_if<is_convertible< | |
327 | ExecutionContext&, execution_context&>::value>::type* = 0); | |
328 | ||
7c673cae FG |
329 | /*@}*/ |
330 | ||
331 | } // namespace asio | |
332 | } // namespace boost | |
333 | ||
334 | #include <boost/asio/detail/pop_options.hpp> | |
335 | ||
336 | #include <boost/asio/impl/spawn.hpp> | |
337 | ||
338 | #endif // BOOST_ASIO_SPAWN_HPP |