File indexing completed on 2025-07-05 08:27:27
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011 #ifndef BOOST_ASIO_IMPL_CO_SPAWN_HPP
0012 #define BOOST_ASIO_IMPL_CO_SPAWN_HPP
0013
0014 #if defined(_MSC_VER) && (_MSC_VER >= 1200)
0015 # pragma once
0016 #endif
0017
0018 #include <boost/asio/detail/config.hpp>
0019 #include <boost/asio/associated_cancellation_slot.hpp>
0020 #include <boost/asio/awaitable.hpp>
0021 #include <boost/asio/detail/memory.hpp>
0022 #include <boost/asio/detail/recycling_allocator.hpp>
0023 #include <boost/asio/dispatch.hpp>
0024 #include <boost/asio/execution/outstanding_work.hpp>
0025 #include <boost/asio/post.hpp>
0026 #include <boost/asio/prefer.hpp>
0027 #include <boost/asio/use_awaitable.hpp>
0028
0029 #include <boost/asio/detail/push_options.hpp>
0030
0031 namespace boost {
0032 namespace asio {
0033 namespace detail {
0034
0035 template <typename Executor, typename = void>
0036 class co_spawn_work_guard
0037 {
0038 public:
0039 typedef decay_t<
0040 prefer_result_t<Executor,
0041 execution::outstanding_work_t::tracked_t
0042 >
0043 > executor_type;
0044
0045 co_spawn_work_guard(const Executor& ex)
0046 : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked))
0047 {
0048 }
0049
0050 executor_type get_executor() const noexcept
0051 {
0052 return executor_;
0053 }
0054
0055 private:
0056 executor_type executor_;
0057 };
0058
0059 #if !defined(BOOST_ASIO_NO_TS_EXECUTORS)
0060
0061 template <typename Executor>
0062 struct co_spawn_work_guard<Executor,
0063 enable_if_t<
0064 !execution::is_executor<Executor>::value
0065 >> : executor_work_guard<Executor>
0066 {
0067 co_spawn_work_guard(const Executor& ex)
0068 : executor_work_guard<Executor>(ex)
0069 {
0070 }
0071 };
0072
0073 #endif
0074
0075 template <typename Handler, typename Executor,
0076 typename Function, typename = void>
0077 struct co_spawn_state
0078 {
0079 template <typename H, typename F>
0080 co_spawn_state(H&& h, const Executor& ex, F&& f)
0081 : handler(std::forward<H>(h)),
0082 spawn_work(ex),
0083 handler_work(boost::asio::get_associated_executor(handler, ex)),
0084 function(std::forward<F>(f))
0085 {
0086 }
0087
0088 Handler handler;
0089 co_spawn_work_guard<Executor> spawn_work;
0090 co_spawn_work_guard<associated_executor_t<Handler, Executor>> handler_work;
0091 Function function;
0092 };
0093
0094 template <typename Handler, typename Executor, typename Function>
0095 struct co_spawn_state<Handler, Executor, Function,
0096 enable_if_t<
0097 is_same<
0098 typename associated_executor<Handler,
0099 Executor>::asio_associated_executor_is_unspecialised,
0100 void
0101 >::value
0102 >>
0103 {
0104 template <typename H, typename F>
0105 co_spawn_state(H&& h, const Executor& ex, F&& f)
0106 : handler(std::forward<H>(h)),
0107 handler_work(ex),
0108 function(std::forward<F>(f))
0109 {
0110 }
0111
0112 Handler handler;
0113 co_spawn_work_guard<Executor> handler_work;
0114 Function function;
0115 };
0116
0117 struct co_spawn_dispatch
0118 {
0119 template <typename CompletionToken>
0120 auto operator()(CompletionToken&& token) const
0121 -> decltype(boost::asio::dispatch(std::forward<CompletionToken>(token)))
0122 {
0123 return boost::asio::dispatch(std::forward<CompletionToken>(token));
0124 }
0125 };
0126
0127 struct co_spawn_post
0128 {
0129 template <typename CompletionToken>
0130 auto operator()(CompletionToken&& token) const
0131 -> decltype(boost::asio::post(std::forward<CompletionToken>(token)))
0132 {
0133 return boost::asio::post(std::forward<CompletionToken>(token));
0134 }
0135 };
0136
0137 template <typename T, typename Handler, typename Executor, typename Function>
0138 awaitable<awaitable_thread_entry_point, Executor> co_spawn_entry_point(
0139 awaitable<T, Executor>*, co_spawn_state<Handler, Executor, Function> s)
0140 {
0141 (void) co_await co_spawn_dispatch{};
0142
0143 (co_await awaitable_thread_has_context_switched{}) = false;
0144 std::exception_ptr e = nullptr;
0145 bool done = false;
0146 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0147 try
0148 #endif
0149 {
0150 T t = co_await s.function();
0151
0152 done = true;
0153
0154 bool switched = (co_await awaitable_thread_has_context_switched{});
0155 if (!switched)
0156 {
0157 co_await this_coro::throw_if_cancelled(false);
0158 (void) co_await co_spawn_post();
0159 }
0160
0161 (dispatch)(s.handler_work.get_executor(),
0162 [handler = std::move(s.handler), t = std::move(t)]() mutable
0163 {
0164 std::move(handler)(std::exception_ptr(), std::move(t));
0165 });
0166
0167 co_return;
0168 }
0169 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0170 catch (...)
0171 {
0172 if (done)
0173 throw;
0174
0175 e = std::current_exception();
0176 }
0177 #endif
0178
0179 bool switched = (co_await awaitable_thread_has_context_switched{});
0180 if (!switched)
0181 {
0182 co_await this_coro::throw_if_cancelled(false);
0183 (void) co_await co_spawn_post();
0184 }
0185
0186 (dispatch)(s.handler_work.get_executor(),
0187 [handler = std::move(s.handler), e]() mutable
0188 {
0189 std::move(handler)(e, T());
0190 });
0191 }
0192
0193 template <typename Handler, typename Executor, typename Function>
0194 awaitable<awaitable_thread_entry_point, Executor> co_spawn_entry_point(
0195 awaitable<void, Executor>*, co_spawn_state<Handler, Executor, Function> s)
0196 {
0197 (void) co_await co_spawn_dispatch{};
0198
0199 (co_await awaitable_thread_has_context_switched{}) = false;
0200 std::exception_ptr e = nullptr;
0201 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0202 try
0203 #endif
0204 {
0205 co_await s.function();
0206 }
0207 #if !defined(BOOST_ASIO_NO_EXCEPTIONS)
0208 catch (...)
0209 {
0210 e = std::current_exception();
0211 }
0212 #endif
0213
0214 bool switched = (co_await awaitable_thread_has_context_switched{});
0215 if (!switched)
0216 {
0217 co_await this_coro::throw_if_cancelled(false);
0218 (void) co_await co_spawn_post();
0219 }
0220
0221 (dispatch)(s.handler_work.get_executor(),
0222 [handler = std::move(s.handler), e]() mutable
0223 {
0224 std::move(handler)(e);
0225 });
0226 }
0227
0228 template <typename T, typename Executor>
0229 class awaitable_as_function
0230 {
0231 public:
0232 explicit awaitable_as_function(awaitable<T, Executor>&& a)
0233 : awaitable_(std::move(a))
0234 {
0235 }
0236
0237 awaitable<T, Executor> operator()()
0238 {
0239 return std::move(awaitable_);
0240 }
0241
0242 private:
0243 awaitable<T, Executor> awaitable_;
0244 };
0245
0246 template <typename Handler, typename Executor, typename = void>
0247 class co_spawn_cancellation_handler
0248 {
0249 public:
0250 co_spawn_cancellation_handler(const Handler&, const Executor& ex)
0251 : signal_(detail::allocate_shared<cancellation_signal>(
0252 detail::recycling_allocator<cancellation_signal,
0253 detail::thread_info_base::cancellation_signal_tag>())),
0254 ex_(ex)
0255 {
0256 }
0257
0258 cancellation_slot slot()
0259 {
0260 return signal_->slot();
0261 }
0262
0263 void operator()(cancellation_type_t type)
0264 {
0265 shared_ptr<cancellation_signal> sig = signal_;
0266 boost::asio::dispatch(ex_, [sig, type]{ sig->emit(type); });
0267 }
0268
0269 private:
0270 shared_ptr<cancellation_signal> signal_;
0271 Executor ex_;
0272 };
0273
0274 template <typename Handler, typename Executor>
0275 class co_spawn_cancellation_handler<Handler, Executor,
0276 enable_if_t<
0277 is_same<
0278 typename associated_executor<Handler,
0279 Executor>::asio_associated_executor_is_unspecialised,
0280 void
0281 >::value
0282 >>
0283 {
0284 public:
0285 co_spawn_cancellation_handler(const Handler&, const Executor&)
0286 {
0287 }
0288
0289 cancellation_slot slot()
0290 {
0291 return signal_.slot();
0292 }
0293
0294 void operator()(cancellation_type_t type)
0295 {
0296 signal_.emit(type);
0297 }
0298
0299 private:
0300 cancellation_signal signal_;
0301 };
0302
0303 template <typename Executor>
0304 class initiate_co_spawn
0305 {
0306 public:
0307 typedef Executor executor_type;
0308
0309 template <typename OtherExecutor>
0310 explicit initiate_co_spawn(const OtherExecutor& ex)
0311 : ex_(ex)
0312 {
0313 }
0314
0315 executor_type get_executor() const noexcept
0316 {
0317 return ex_;
0318 }
0319
0320 template <typename Handler, typename F>
0321 void operator()(Handler&& handler, F&& f) const
0322 {
0323 typedef result_of_t<F()> awaitable_type;
0324 typedef decay_t<Handler> handler_type;
0325 typedef decay_t<F> function_type;
0326 typedef co_spawn_cancellation_handler<
0327 handler_type, Executor> cancel_handler_type;
0328
0329 auto slot = boost::asio::get_associated_cancellation_slot(handler);
0330 cancel_handler_type* cancel_handler = slot.is_connected()
0331 ? &slot.template emplace<cancel_handler_type>(handler, ex_)
0332 : nullptr;
0333
0334 cancellation_slot proxy_slot(
0335 cancel_handler
0336 ? cancel_handler->slot()
0337 : cancellation_slot());
0338
0339 cancellation_state cancel_state(proxy_slot);
0340
0341 auto a = (co_spawn_entry_point)(static_cast<awaitable_type*>(nullptr),
0342 co_spawn_state<handler_type, Executor, function_type>(
0343 std::forward<Handler>(handler), ex_, std::forward<F>(f)));
0344 awaitable_handler<executor_type, void>(std::move(a),
0345 ex_, proxy_slot, cancel_state).launch();
0346 }
0347
0348 private:
0349 Executor ex_;
0350 };
0351
0352 }
0353
0354 template <typename Executor, typename T, typename AwaitableExecutor,
0355 BOOST_ASIO_COMPLETION_TOKEN_FOR(
0356 void(std::exception_ptr, T)) CompletionToken>
0357 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
0358 CompletionToken, void(std::exception_ptr, T))
0359 co_spawn(const Executor& ex,
0360 awaitable<T, AwaitableExecutor> a, CompletionToken&& token,
0361 constraint_t<
0362 (is_executor<Executor>::value || execution::is_executor<Executor>::value)
0363 && is_convertible<Executor, AwaitableExecutor>::value
0364 >)
0365 {
0366 return async_initiate<CompletionToken, void(std::exception_ptr, T)>(
0367 detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)),
0368 token, detail::awaitable_as_function<T, AwaitableExecutor>(std::move(a)));
0369 }
0370
0371 template <typename Executor, typename AwaitableExecutor,
0372 BOOST_ASIO_COMPLETION_TOKEN_FOR(
0373 void(std::exception_ptr)) CompletionToken>
0374 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
0375 CompletionToken, void(std::exception_ptr))
0376 co_spawn(const Executor& ex,
0377 awaitable<void, AwaitableExecutor> a, CompletionToken&& token,
0378 constraint_t<
0379 (is_executor<Executor>::value || execution::is_executor<Executor>::value)
0380 && is_convertible<Executor, AwaitableExecutor>::value
0381 >)
0382 {
0383 return async_initiate<CompletionToken, void(std::exception_ptr)>(
0384 detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)),
0385 token, detail::awaitable_as_function<
0386 void, AwaitableExecutor>(std::move(a)));
0387 }
0388
0389 template <typename ExecutionContext, typename T, typename AwaitableExecutor,
0390 BOOST_ASIO_COMPLETION_TOKEN_FOR(
0391 void(std::exception_ptr, T)) CompletionToken>
0392 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
0393 CompletionToken, void(std::exception_ptr, T))
0394 co_spawn(ExecutionContext& ctx,
0395 awaitable<T, AwaitableExecutor> a, CompletionToken&& token,
0396 constraint_t<
0397 is_convertible<ExecutionContext&, execution_context&>::value
0398 && is_convertible<typename ExecutionContext::executor_type,
0399 AwaitableExecutor>::value
0400 >)
0401 {
0402 return (co_spawn)(ctx.get_executor(), std::move(a),
0403 std::forward<CompletionToken>(token));
0404 }
0405
0406 template <typename ExecutionContext, typename AwaitableExecutor,
0407 BOOST_ASIO_COMPLETION_TOKEN_FOR(
0408 void(std::exception_ptr)) CompletionToken>
0409 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
0410 CompletionToken, void(std::exception_ptr))
0411 co_spawn(ExecutionContext& ctx,
0412 awaitable<void, AwaitableExecutor> a, CompletionToken&& token,
0413 constraint_t<
0414 is_convertible<ExecutionContext&, execution_context&>::value
0415 && is_convertible<typename ExecutionContext::executor_type,
0416 AwaitableExecutor>::value
0417 >)
0418 {
0419 return (co_spawn)(ctx.get_executor(), std::move(a),
0420 std::forward<CompletionToken>(token));
0421 }
0422
0423 template <typename Executor, typename F,
0424 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature<
0425 result_of_t<F()>>::type) CompletionToken>
0426 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
0427 typename detail::awaitable_signature<result_of_t<F()>>::type)
0428 co_spawn(const Executor& ex, F&& f, CompletionToken&& token,
0429 constraint_t<
0430 is_executor<Executor>::value || execution::is_executor<Executor>::value
0431 >)
0432 {
0433 return async_initiate<CompletionToken,
0434 typename detail::awaitable_signature<result_of_t<F()>>::type>(
0435 detail::initiate_co_spawn<
0436 typename result_of_t<F()>::executor_type>(ex),
0437 token, std::forward<F>(f));
0438 }
0439
0440 template <typename ExecutionContext, typename F,
0441 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature<
0442 result_of_t<F()>>::type) CompletionToken>
0443 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
0444 typename detail::awaitable_signature<result_of_t<F()>>::type)
0445 co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token,
0446 constraint_t<
0447 is_convertible<ExecutionContext&, execution_context&>::value
0448 >)
0449 {
0450 return (co_spawn)(ctx.get_executor(), std::forward<F>(f),
0451 std::forward<CompletionToken>(token));
0452 }
0453
0454 }
0455 }
0456
0457 #include <boost/asio/detail/pop_options.hpp>
0458
0459 #endif