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