File indexing completed on 2025-01-18 09:28:54
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 try
0147 {
0148 T t = co_await s.function();
0149
0150 done = true;
0151
0152 bool switched = (co_await awaitable_thread_has_context_switched{});
0153 if (!switched)
0154 (void) co_await co_spawn_post();
0155
0156 (dispatch)(s.handler_work.get_executor(),
0157 [handler = std::move(s.handler), t = std::move(t)]() mutable
0158 {
0159 std::move(handler)(std::exception_ptr(), std::move(t));
0160 });
0161
0162 co_return;
0163 }
0164 catch (...)
0165 {
0166 if (done)
0167 throw;
0168
0169 e = std::current_exception();
0170 }
0171
0172 bool switched = (co_await awaitable_thread_has_context_switched{});
0173 if (!switched)
0174 (void) co_await co_spawn_post();
0175
0176 (dispatch)(s.handler_work.get_executor(),
0177 [handler = std::move(s.handler), e]() mutable
0178 {
0179 std::move(handler)(e, T());
0180 });
0181 }
0182
0183 template <typename Handler, typename Executor, typename Function>
0184 awaitable<awaitable_thread_entry_point, Executor> co_spawn_entry_point(
0185 awaitable<void, Executor>*, co_spawn_state<Handler, Executor, Function> s)
0186 {
0187 (void) co_await co_spawn_dispatch{};
0188
0189 (co_await awaitable_thread_has_context_switched{}) = false;
0190 std::exception_ptr e = nullptr;
0191 try
0192 {
0193 co_await s.function();
0194 }
0195 catch (...)
0196 {
0197 e = std::current_exception();
0198 }
0199
0200 bool switched = (co_await awaitable_thread_has_context_switched{});
0201 if (!switched)
0202 (void) co_await co_spawn_post();
0203
0204 (dispatch)(s.handler_work.get_executor(),
0205 [handler = std::move(s.handler), e]() mutable
0206 {
0207 std::move(handler)(e);
0208 });
0209 }
0210
0211 template <typename T, typename Executor>
0212 class awaitable_as_function
0213 {
0214 public:
0215 explicit awaitable_as_function(awaitable<T, Executor>&& a)
0216 : awaitable_(std::move(a))
0217 {
0218 }
0219
0220 awaitable<T, Executor> operator()()
0221 {
0222 return std::move(awaitable_);
0223 }
0224
0225 private:
0226 awaitable<T, Executor> awaitable_;
0227 };
0228
0229 template <typename Handler, typename Executor, typename = void>
0230 class co_spawn_cancellation_handler
0231 {
0232 public:
0233 co_spawn_cancellation_handler(const Handler&, const Executor& ex)
0234 : signal_(detail::allocate_shared<cancellation_signal>(
0235 detail::recycling_allocator<cancellation_signal,
0236 detail::thread_info_base::cancellation_signal_tag>())),
0237 ex_(ex)
0238 {
0239 }
0240
0241 cancellation_slot slot()
0242 {
0243 return signal_->slot();
0244 }
0245
0246 void operator()(cancellation_type_t type)
0247 {
0248 shared_ptr<cancellation_signal> sig = signal_;
0249 boost::asio::dispatch(ex_, [sig, type]{ sig->emit(type); });
0250 }
0251
0252 private:
0253 shared_ptr<cancellation_signal> signal_;
0254 Executor ex_;
0255 };
0256
0257 template <typename Handler, typename Executor>
0258 class co_spawn_cancellation_handler<Handler, Executor,
0259 enable_if_t<
0260 is_same<
0261 typename associated_executor<Handler,
0262 Executor>::asio_associated_executor_is_unspecialised,
0263 void
0264 >::value
0265 >>
0266 {
0267 public:
0268 co_spawn_cancellation_handler(const Handler&, const Executor&)
0269 {
0270 }
0271
0272 cancellation_slot slot()
0273 {
0274 return signal_.slot();
0275 }
0276
0277 void operator()(cancellation_type_t type)
0278 {
0279 signal_.emit(type);
0280 }
0281
0282 private:
0283 cancellation_signal signal_;
0284 };
0285
0286 template <typename Executor>
0287 class initiate_co_spawn
0288 {
0289 public:
0290 typedef Executor executor_type;
0291
0292 template <typename OtherExecutor>
0293 explicit initiate_co_spawn(const OtherExecutor& ex)
0294 : ex_(ex)
0295 {
0296 }
0297
0298 executor_type get_executor() const noexcept
0299 {
0300 return ex_;
0301 }
0302
0303 template <typename Handler, typename F>
0304 void operator()(Handler&& handler, F&& f) const
0305 {
0306 typedef result_of_t<F()> awaitable_type;
0307 typedef decay_t<Handler> handler_type;
0308 typedef decay_t<F> function_type;
0309 typedef co_spawn_cancellation_handler<
0310 handler_type, Executor> cancel_handler_type;
0311
0312 auto slot = boost::asio::get_associated_cancellation_slot(handler);
0313 cancel_handler_type* cancel_handler = slot.is_connected()
0314 ? &slot.template emplace<cancel_handler_type>(handler, ex_)
0315 : nullptr;
0316
0317 cancellation_slot proxy_slot(
0318 cancel_handler
0319 ? cancel_handler->slot()
0320 : cancellation_slot());
0321
0322 cancellation_state cancel_state(proxy_slot);
0323
0324 auto a = (co_spawn_entry_point)(static_cast<awaitable_type*>(nullptr),
0325 co_spawn_state<handler_type, Executor, function_type>(
0326 std::forward<Handler>(handler), ex_, std::forward<F>(f)));
0327 awaitable_handler<executor_type, void>(std::move(a),
0328 ex_, proxy_slot, cancel_state).launch();
0329 }
0330
0331 private:
0332 Executor ex_;
0333 };
0334
0335 }
0336
0337 template <typename Executor, typename T, typename AwaitableExecutor,
0338 BOOST_ASIO_COMPLETION_TOKEN_FOR(
0339 void(std::exception_ptr, T)) CompletionToken>
0340 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
0341 CompletionToken, void(std::exception_ptr, T))
0342 co_spawn(const Executor& ex,
0343 awaitable<T, AwaitableExecutor> a, CompletionToken&& token,
0344 constraint_t<
0345 (is_executor<Executor>::value || execution::is_executor<Executor>::value)
0346 && is_convertible<Executor, AwaitableExecutor>::value
0347 >)
0348 {
0349 return async_initiate<CompletionToken, void(std::exception_ptr, T)>(
0350 detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)),
0351 token, detail::awaitable_as_function<T, AwaitableExecutor>(std::move(a)));
0352 }
0353
0354 template <typename Executor, typename AwaitableExecutor,
0355 BOOST_ASIO_COMPLETION_TOKEN_FOR(
0356 void(std::exception_ptr)) CompletionToken>
0357 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
0358 CompletionToken, void(std::exception_ptr))
0359 co_spawn(const Executor& ex,
0360 awaitable<void, 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)>(
0367 detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)),
0368 token, detail::awaitable_as_function<
0369 void, AwaitableExecutor>(std::move(a)));
0370 }
0371
0372 template <typename ExecutionContext, typename T, typename AwaitableExecutor,
0373 BOOST_ASIO_COMPLETION_TOKEN_FOR(
0374 void(std::exception_ptr, T)) CompletionToken>
0375 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
0376 CompletionToken, void(std::exception_ptr, T))
0377 co_spawn(ExecutionContext& ctx,
0378 awaitable<T, AwaitableExecutor> a, CompletionToken&& token,
0379 constraint_t<
0380 is_convertible<ExecutionContext&, execution_context&>::value
0381 && is_convertible<typename ExecutionContext::executor_type,
0382 AwaitableExecutor>::value
0383 >)
0384 {
0385 return (co_spawn)(ctx.get_executor(), std::move(a),
0386 std::forward<CompletionToken>(token));
0387 }
0388
0389 template <typename ExecutionContext, typename AwaitableExecutor,
0390 BOOST_ASIO_COMPLETION_TOKEN_FOR(
0391 void(std::exception_ptr)) CompletionToken>
0392 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(
0393 CompletionToken, void(std::exception_ptr))
0394 co_spawn(ExecutionContext& ctx,
0395 awaitable<void, 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 Executor, typename F,
0407 BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature<
0408 result_of_t<F()>>::type) CompletionToken>
0409 inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken,
0410 typename detail::awaitable_signature<result_of_t<F()>>::type)
0411 co_spawn(const Executor& ex, F&& f, CompletionToken&& token,
0412 constraint_t<
0413 is_executor<Executor>::value || execution::is_executor<Executor>::value
0414 >)
0415 {
0416 return async_initiate<CompletionToken,
0417 typename detail::awaitable_signature<result_of_t<F()>>::type>(
0418 detail::initiate_co_spawn<
0419 typename result_of_t<F()>::executor_type>(ex),
0420 token, std::forward<F>(f));
0421 }
0422
0423 template <typename ExecutionContext, 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(ExecutionContext& ctx, F&& f, CompletionToken&& token,
0429 constraint_t<
0430 is_convertible<ExecutionContext&, execution_context&>::value
0431 >)
0432 {
0433 return (co_spawn)(ctx.get_executor(), std::forward<F>(f),
0434 std::forward<CompletionToken>(token));
0435 }
0436
0437 }
0438 }
0439
0440 #include <boost/asio/detail/pop_options.hpp>
0441
0442 #endif