| // |
| // impl/co_spawn.hpp |
| // ~~~~~~~~~~~~~~~~~ |
| // |
| // Copyright (c) 2003-2021 Christopher M. Kohlhoff (chris at kohlhoff dot com) |
| // |
| // Distributed under the Boost Software License, Version 1.0. (See accompanying |
| // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) |
| // |
| |
| #ifndef BOOST_ASIO_IMPL_CO_SPAWN_HPP |
| #define BOOST_ASIO_IMPL_CO_SPAWN_HPP |
| |
| #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
| # pragma once |
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
| |
| #include <boost/asio/detail/config.hpp> |
| #include <boost/asio/awaitable.hpp> |
| #include <boost/asio/dispatch.hpp> |
| #include <boost/asio/execution/outstanding_work.hpp> |
| #include <boost/asio/post.hpp> |
| #include <boost/asio/prefer.hpp> |
| #include <boost/asio/use_awaitable.hpp> |
| |
| #include <boost/asio/detail/push_options.hpp> |
| |
| namespace boost { |
| namespace asio { |
| namespace detail { |
| |
| template <typename Executor, typename = void> |
| class co_spawn_work_guard |
| { |
| public: |
| typedef typename decay< |
| typename prefer_result<Executor, |
| execution::outstanding_work_t::tracked_t |
| >::type |
| >::type executor_type; |
| |
| co_spawn_work_guard(const Executor& ex) |
| : executor_(boost::asio::prefer(ex, execution::outstanding_work.tracked)) |
| { |
| } |
| |
| executor_type get_executor() const BOOST_ASIO_NOEXCEPT |
| { |
| return executor_; |
| } |
| |
| private: |
| executor_type executor_; |
| }; |
| |
| #if !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
| |
| template <typename Executor> |
| struct co_spawn_work_guard<Executor, |
| typename enable_if< |
| !execution::is_executor<Executor>::value |
| >::type> : executor_work_guard<Executor> |
| { |
| co_spawn_work_guard(const Executor& ex) |
| : executor_work_guard<Executor>(ex) |
| { |
| } |
| }; |
| |
| #endif // !defined(BOOST_ASIO_NO_TS_EXECUTORS) |
| |
| template <typename Executor> |
| inline co_spawn_work_guard<Executor> |
| make_co_spawn_work_guard(const Executor& ex) |
| { |
| return co_spawn_work_guard<Executor>(ex); |
| } |
| |
| template <typename T, typename Executor, typename F, typename Handler> |
| awaitable<void, Executor> co_spawn_entry_point( |
| awaitable<T, Executor>*, Executor ex, F f, Handler handler) |
| { |
| auto spawn_work = make_co_spawn_work_guard(ex); |
| auto handler_work = make_co_spawn_work_guard( |
| boost::asio::get_associated_executor(handler, ex)); |
| |
| (void) co_await (post)(spawn_work.get_executor(), |
| use_awaitable_t<Executor>{}); |
| |
| bool done = false; |
| try |
| { |
| T t = co_await f(); |
| |
| done = true; |
| |
| (dispatch)(handler_work.get_executor(), |
| [handler = std::move(handler), t = std::move(t)]() mutable |
| { |
| handler(std::exception_ptr(), std::move(t)); |
| }); |
| } |
| catch (...) |
| { |
| if (done) |
| throw; |
| |
| (dispatch)(handler_work.get_executor(), |
| [handler = std::move(handler), e = std::current_exception()]() mutable |
| { |
| handler(e, T()); |
| }); |
| } |
| } |
| |
| template <typename Executor, typename F, typename Handler> |
| awaitable<void, Executor> co_spawn_entry_point( |
| awaitable<void, Executor>*, Executor ex, F f, Handler handler) |
| { |
| auto spawn_work = make_co_spawn_work_guard(ex); |
| auto handler_work = make_co_spawn_work_guard( |
| boost::asio::get_associated_executor(handler, ex)); |
| |
| (void) co_await (post)(spawn_work.get_executor(), |
| use_awaitable_t<Executor>{__FILE__, __LINE__, "co_spawn_entry_point"}); |
| |
| std::exception_ptr e = nullptr; |
| try |
| { |
| co_await f(); |
| } |
| catch (...) |
| { |
| e = std::current_exception(); |
| } |
| |
| (dispatch)(handler_work.get_executor(), |
| [handler = std::move(handler), e]() mutable |
| { |
| handler(e); |
| }); |
| } |
| |
| template <typename T, typename Executor> |
| class awaitable_as_function |
| { |
| public: |
| explicit awaitable_as_function(awaitable<T, Executor>&& a) |
| : awaitable_(std::move(a)) |
| { |
| } |
| |
| awaitable<T, Executor> operator()() |
| { |
| return std::move(awaitable_); |
| } |
| |
| private: |
| awaitable<T, Executor> awaitable_; |
| }; |
| |
| template <typename Executor> |
| class initiate_co_spawn |
| { |
| public: |
| typedef Executor executor_type; |
| |
| template <typename OtherExecutor> |
| explicit initiate_co_spawn(const OtherExecutor& ex) |
| : ex_(ex) |
| { |
| } |
| |
| executor_type get_executor() const BOOST_ASIO_NOEXCEPT |
| { |
| return ex_; |
| } |
| |
| template <typename Handler, typename F> |
| void operator()(Handler&& handler, F&& f) const |
| { |
| typedef typename result_of<F()>::type awaitable_type; |
| |
| auto a = (co_spawn_entry_point)(static_cast<awaitable_type*>(nullptr), |
| ex_, std::forward<F>(f), std::forward<Handler>(handler)); |
| awaitable_handler<executor_type, void>(std::move(a), ex_).launch(); |
| } |
| |
| private: |
| Executor ex_; |
| }; |
| |
| } // namespace detail |
| |
| template <typename Executor, typename T, typename AwaitableExecutor, |
| BOOST_ASIO_COMPLETION_TOKEN_FOR( |
| void(std::exception_ptr, T)) CompletionToken> |
| inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( |
| CompletionToken, void(std::exception_ptr, T)) |
| co_spawn(const Executor& ex, |
| awaitable<T, AwaitableExecutor> a, CompletionToken&& token, |
| typename constraint< |
| (is_executor<Executor>::value || execution::is_executor<Executor>::value) |
| && is_convertible<Executor, AwaitableExecutor>::value |
| >::type) |
| { |
| return async_initiate<CompletionToken, void(std::exception_ptr, T)>( |
| detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)), |
| token, detail::awaitable_as_function<T, AwaitableExecutor>(std::move(a))); |
| } |
| |
| template <typename Executor, typename AwaitableExecutor, |
| BOOST_ASIO_COMPLETION_TOKEN_FOR( |
| void(std::exception_ptr)) CompletionToken> |
| inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( |
| CompletionToken, void(std::exception_ptr)) |
| co_spawn(const Executor& ex, |
| awaitable<void, AwaitableExecutor> a, CompletionToken&& token, |
| typename constraint< |
| (is_executor<Executor>::value || execution::is_executor<Executor>::value) |
| && is_convertible<Executor, AwaitableExecutor>::value |
| >::type) |
| { |
| return async_initiate<CompletionToken, void(std::exception_ptr)>( |
| detail::initiate_co_spawn<AwaitableExecutor>(AwaitableExecutor(ex)), |
| token, detail::awaitable_as_function< |
| void, AwaitableExecutor>(std::move(a))); |
| } |
| |
| template <typename ExecutionContext, typename T, typename AwaitableExecutor, |
| BOOST_ASIO_COMPLETION_TOKEN_FOR( |
| void(std::exception_ptr, T)) CompletionToken> |
| inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( |
| CompletionToken, void(std::exception_ptr, T)) |
| co_spawn(ExecutionContext& ctx, |
| awaitable<T, AwaitableExecutor> a, CompletionToken&& token, |
| typename constraint< |
| is_convertible<ExecutionContext&, execution_context&>::value |
| && is_convertible<typename ExecutionContext::executor_type, |
| AwaitableExecutor>::value |
| >::type) |
| { |
| return (co_spawn)(ctx.get_executor(), std::move(a), |
| std::forward<CompletionToken>(token)); |
| } |
| |
| template <typename ExecutionContext, typename AwaitableExecutor, |
| BOOST_ASIO_COMPLETION_TOKEN_FOR( |
| void(std::exception_ptr)) CompletionToken> |
| inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE( |
| CompletionToken, void(std::exception_ptr)) |
| co_spawn(ExecutionContext& ctx, |
| awaitable<void, AwaitableExecutor> a, CompletionToken&& token, |
| typename constraint< |
| is_convertible<ExecutionContext&, execution_context&>::value |
| && is_convertible<typename ExecutionContext::executor_type, |
| AwaitableExecutor>::value |
| >::type) |
| { |
| return (co_spawn)(ctx.get_executor(), std::move(a), |
| std::forward<CompletionToken>(token)); |
| } |
| |
| template <typename Executor, typename F, |
| BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature< |
| typename result_of<F()>::type>::type) CompletionToken> |
| inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, |
| typename detail::awaitable_signature<typename result_of<F()>::type>::type) |
| co_spawn(const Executor& ex, F&& f, CompletionToken&& token, |
| typename constraint< |
| is_executor<Executor>::value || execution::is_executor<Executor>::value |
| >::type) |
| { |
| return async_initiate<CompletionToken, |
| typename detail::awaitable_signature<typename result_of<F()>::type>::type>( |
| detail::initiate_co_spawn< |
| typename result_of<F()>::type::executor_type>(ex), |
| token, std::forward<F>(f)); |
| } |
| |
| template <typename ExecutionContext, typename F, |
| BOOST_ASIO_COMPLETION_TOKEN_FOR(typename detail::awaitable_signature< |
| typename result_of<F()>::type>::type) CompletionToken> |
| inline BOOST_ASIO_INITFN_AUTO_RESULT_TYPE(CompletionToken, |
| typename detail::awaitable_signature<typename result_of<F()>::type>::type) |
| co_spawn(ExecutionContext& ctx, F&& f, CompletionToken&& token, |
| typename constraint< |
| is_convertible<ExecutionContext&, execution_context&>::value |
| >::type) |
| { |
| return (co_spawn)(ctx.get_executor(), std::forward<F>(f), |
| std::forward<CompletionToken>(token)); |
| } |
| |
| } // namespace asio |
| } // namespace boost |
| |
| #include <boost/asio/detail/pop_options.hpp> |
| |
| #endif // BOOST_ASIO_IMPL_CO_SPAWN_HPP |