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