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