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