• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 //
2 // spawn.hpp
3 // ~~~~~~~~~
4 //
5 // Copyright (c) 2003-2021 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 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>
20 #include <boost/asio/any_io_executor.hpp>
21 #include <boost/asio/bind_executor.hpp>
22 #include <boost/asio/detail/memory.hpp>
23 #include <boost/asio/detail/type_traits.hpp>
24 #include <boost/asio/detail/wrapped_handler.hpp>
25 #include <boost/asio/io_context.hpp>
26 #include <boost/asio/is_executor.hpp>
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    */
basic_yield_context(const detail::weak_ptr<callee_type> & coro,caller_type & ca,Handler & handler)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 
102   /// Construct a yield context from another yield context type.
103   /**
104    * Requires that OtherHandler be convertible to Handler.
105    */
106   template <typename OtherHandler>
basic_yield_context(const basic_yield_context<OtherHandler> & other)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 
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    */
operator [](boost::system::error_code & ec) const134   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_;
146   Handler handler_;
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<
155   executor_binder<void(*)(), any_io_executor> > yield_context;
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 
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 
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
228       = boost::coroutines::attributes(),
229     typename constraint<
230       !is_executor<typename decay<Handler>::type>::value &&
231       !execution::is_executor<typename decay<Handler>::type>::value &&
232       !is_convertible<Handler&, execution_context&>::value>::type = 0);
233 
234 /// Start a new stackful coroutine, inheriting the execution context of another.
235 /**
236  * This function is used to launch a new coroutine.
237  *
238  * @param ctx Identifies the current coroutine as a parent of the new
239  * coroutine. This specifies that the new coroutine should inherit the
240  * execution context of the parent. For example, if the parent coroutine is
241  * executing in a particular strand, then the new coroutine will execute in the
242  * same strand.
243  *
244  * @param function The coroutine function. The function must have the signature:
245  * @code void function(basic_yield_context<Handler> yield); @endcode
246  *
247  * @param attributes Boost.Coroutine attributes used to customise the coroutine.
248  */
249 template <typename Handler, typename Function>
250 void spawn(basic_yield_context<Handler> ctx,
251     BOOST_ASIO_MOVE_ARG(Function) function,
252     const boost::coroutines::attributes& attributes
253       = boost::coroutines::attributes());
254 
255 /// Start a new stackful coroutine that executes on a given executor.
256 /**
257  * This function is used to launch a new coroutine.
258  *
259  * @param ex Identifies the executor that will run the coroutine. The new
260  * coroutine is implicitly given its own strand within this executor.
261  *
262  * @param function The coroutine function. The function must have the signature:
263  * @code void function(yield_context yield); @endcode
264  *
265  * @param attributes Boost.Coroutine attributes used to customise the coroutine.
266  */
267 template <typename Function, typename Executor>
268 void spawn(const Executor& ex,
269     BOOST_ASIO_MOVE_ARG(Function) function,
270     const boost::coroutines::attributes& attributes
271       = boost::coroutines::attributes(),
272     typename constraint<
273       is_executor<Executor>::value || execution::is_executor<Executor>::value
274     >::type = 0);
275 
276 /// Start a new stackful coroutine that executes on a given strand.
277 /**
278  * This function is used to launch a new coroutine.
279  *
280  * @param ex Identifies the strand that will run the coroutine.
281  *
282  * @param function The coroutine function. The function must have the signature:
283  * @code void function(yield_context yield); @endcode
284  *
285  * @param attributes Boost.Coroutine attributes used to customise the coroutine.
286  */
287 template <typename Function, typename Executor>
288 void spawn(const strand<Executor>& ex,
289     BOOST_ASIO_MOVE_ARG(Function) function,
290     const boost::coroutines::attributes& attributes
291       = boost::coroutines::attributes());
292 
293 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
294 
295 /// Start a new stackful coroutine that executes in the context of a strand.
296 /**
297  * This function is used to launch a new coroutine.
298  *
299  * @param s Identifies a strand. By starting multiple coroutines on the same
300  * strand, the implementation ensures that none of those coroutines can execute
301  * simultaneously.
302  *
303  * @param function The coroutine function. The function must have the signature:
304  * @code void function(yield_context yield); @endcode
305  *
306  * @param attributes Boost.Coroutine attributes used to customise the coroutine.
307  */
308 template <typename Function>
309 void spawn(const boost::asio::io_context::strand& s,
310     BOOST_ASIO_MOVE_ARG(Function) function,
311     const boost::coroutines::attributes& attributes
312       = boost::coroutines::attributes());
313 
314 #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
315 
316 /// Start a new stackful coroutine that executes on a given execution context.
317 /**
318  * This function is used to launch a new coroutine.
319  *
320  * @param ctx Identifies the execution context that will run the coroutine. The
321  * new coroutine is implicitly given its own strand within this execution
322  * context.
323  *
324  * @param function The coroutine function. The function must have the signature:
325  * @code void function(yield_context yield); @endcode
326  *
327  * @param attributes Boost.Coroutine attributes used to customise the coroutine.
328  */
329 template <typename Function, typename ExecutionContext>
330 void spawn(ExecutionContext& ctx,
331     BOOST_ASIO_MOVE_ARG(Function) function,
332     const boost::coroutines::attributes& attributes
333       = boost::coroutines::attributes(),
334     typename constraint<is_convertible<
335       ExecutionContext&, execution_context&>::value>::type = 0);
336 
337 /*@}*/
338 
339 } // namespace asio
340 } // namespace boost
341 
342 #include <boost/asio/detail/pop_options.hpp>
343 
344 #include <boost/asio/impl/spawn.hpp>
345 
346 #endif // BOOST_ASIO_SPAWN_HPP
347