Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-01-18 09:28:54

0001 //
0002 // impl/co_spawn.hpp
0003 // ~~~~~~~~~~~~~~~~~
0004 //
0005 // Copyright (c) 2003-2023 Christopher M. Kohlhoff (chris at kohlhoff dot com)
0006 //
0007 // Distributed under the Boost Software License, Version 1.0. (See accompanying
0008 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
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 // defined(_MSC_VER) && (_MSC_VER >= 1200)
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 // !defined(BOOST_ASIO_NO_TS_EXECUTORS)
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 } // namespace detail
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 } // namespace asio
0438 } // namespace boost
0439 
0440 #include <boost/asio/detail/pop_options.hpp>
0441 
0442 #endif // BOOST_ASIO_IMPL_CO_SPAWN_HPP