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