| // |
| // impl/await.hpp |
| // ~~~~~~~~~~~~~~ |
| // |
| // Copyright (c) 2003-2015 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 ASIO_IMPL_AWAIT_HPP |
| #define ASIO_IMPL_AWAIT_HPP |
| |
| #if defined(_MSC_VER) && (_MSC_VER >= 1200) |
| # pragma once |
| #endif // defined(_MSC_VER) && (_MSC_VER >= 1200) |
| |
| #include "asio/detail/config.hpp" |
| #include <exception> |
| #include <functional> |
| #include <memory> |
| #include <new> |
| #include <tuple> |
| #include <utility> |
| #include "asio/async_result.hpp" |
| #include "asio/detail/type_traits.hpp" |
| #include "asio/dispatch.hpp" |
| #include "asio/post.hpp" |
| |
| #include "asio/detail/push_options.hpp" |
| |
| namespace asio { |
| namespace detail { |
| |
| // Promise object for coroutine at top of thread-of-execution "stack". |
| class awaiter |
| { |
| public: |
| awaiter* get_return_object() |
| { |
| return this; |
| } |
| |
| auto initial_suspend() |
| { |
| return std::experimental::suspend_never(); |
| } |
| |
| auto final_suspend() |
| { |
| return std::experimental::suspend_always(); |
| } |
| |
| void set_exception(std::exception_ptr ex) |
| { |
| pending_exception_ = ex; |
| } |
| |
| void return_void() |
| { |
| } |
| |
| awaiter* add_ref() |
| { |
| ++ref_count_; |
| return this; |
| } |
| |
| void release() |
| { |
| if (--ref_count_ == 0) |
| coroutine_handle<awaiter>::from_promise(this).destroy(); |
| } |
| |
| void rethrow_exception() |
| { |
| if (pending_exception_) |
| { |
| std::exception_ptr ex = std::exchange(pending_exception_, nullptr); |
| std::rethrow_exception(ex); |
| } |
| } |
| |
| private: |
| std::size_t ref_count_ = 0; |
| std::exception_ptr pending_exception_; |
| }; |
| |
| struct awaiter_delete |
| { |
| public: |
| void operator()(awaiter* a) |
| { |
| if (a) |
| a->release(); |
| } |
| }; |
| |
| typedef std::unique_ptr<awaiter, awaiter_delete> awaiter_ptr; |
| |
| class awaitee_base |
| { |
| public: |
| auto initial_suspend() |
| { |
| return std::experimental::suspend_never(); |
| } |
| |
| struct final_suspender |
| { |
| awaitee_base* this_; |
| |
| bool await_ready() |
| { |
| return false; |
| } |
| |
| void await_suspend(coroutine_handle<void>) |
| { |
| this_->wake_caller(); |
| } |
| |
| void await_resume() |
| { |
| } |
| }; |
| |
| auto final_suspend() |
| { |
| return final_suspender{this}; |
| } |
| |
| void set_exception(std::exception_ptr e) |
| { |
| pending_exception_ = e; |
| ready_ = true; |
| } |
| |
| void wake_caller() |
| { |
| if (caller_) |
| caller_.resume(); |
| } |
| |
| protected: |
| void rethrow_exception() |
| { |
| if (pending_exception_) |
| { |
| std::exception_ptr ex = std::exchange(pending_exception_, nullptr); |
| std::rethrow_exception(ex); |
| } |
| } |
| |
| template <typename> friend class awaitable; |
| awaiter* awaiter_ = nullptr; |
| coroutine_handle<void> caller_ = nullptr; |
| std::exception_ptr pending_exception_ = nullptr; |
| bool ready_ = false; |
| }; |
| |
| template <typename T> |
| class awaitee |
| : public awaitee_base |
| { |
| public: |
| ~awaitee() |
| { |
| if (initialised_) |
| { |
| T* p = static_cast<T*>(static_cast<void*>(buf_)); |
| p->~T(); |
| } |
| } |
| |
| awaitable<T> get_return_object() |
| { |
| return awaitable<T>(this); |
| }; |
| |
| template <typename U> |
| void return_value(U&& u) |
| { |
| T* p = static_cast<T*>(static_cast<void*>(buf_)); |
| new (p) T(std::forward<U>(u)); |
| initialised_ = true; |
| } |
| |
| T value() |
| { |
| rethrow_exception(); |
| return std::move(*static_cast<T*>(static_cast<void*>(buf_))); |
| } |
| |
| private: |
| template <typename> friend class awaitable; |
| unsigned char buf_[sizeof(T)] alignas(T); |
| bool initialised_ = false; |
| }; |
| |
| template <> |
| class awaitee<void> |
| : public awaitee_base |
| { |
| public: |
| awaitable<void> get_return_object() |
| { |
| return awaitable<void>(this); |
| }; |
| |
| void return_void() |
| { |
| } |
| |
| void value() |
| { |
| rethrow_exception(); |
| } |
| }; |
| |
| #if defined(_MSC_VER) |
| # pragma warning(push) |
| # pragma warning(disable:4033) |
| #endif // defined(_MSC_VER) |
| |
| template <typename T> T dummy_return() |
| { |
| return static_cast<T&&>(*static_cast<T*>(nullptr)); |
| } |
| |
| template <> void dummy_return<void>() |
| { |
| } |
| |
| template <typename T> |
| awaitable<T> make_dummy_awaitable() |
| { |
| for (;;) co_await std::experimental::suspend_always(); |
| return dummy_return<T>(); |
| } |
| |
| #if defined(_MSC_VER) |
| # pragma warning(pop) |
| #endif // defined(_MSC_VER) |
| |
| template <typename Executor> |
| class destroy_awaiter |
| { |
| public: |
| typedef Executor executor_type; |
| |
| destroy_awaiter(Executor ex, awaiter_ptr a) |
| : ex_(ex), |
| awaiter_(std::move(a)) |
| { |
| } |
| |
| destroy_awaiter(destroy_awaiter&& other) |
| : ex_(std::move(other.ex_)), |
| awaiter_(std::move(other.awaiter_)) |
| { |
| } |
| |
| executor_type get_executor() const noexcept |
| { |
| return ex_; |
| } |
| |
| void operator()() |
| { |
| awaiter_ptr(std::move(awaiter_)); |
| } |
| |
| protected: |
| Executor ex_; |
| awaiter_ptr awaiter_; |
| }; |
| |
| template <typename Executor> |
| class awaiter_task |
| { |
| public: |
| typedef Executor executor_type; |
| |
| awaiter_task(Executor ex, awaiter* a) |
| : ex_(ex), |
| awaiter_(a->add_ref()) |
| { |
| } |
| |
| awaiter_task(awaiter_task&& other) |
| : ex_(std::move(other.ex_)), |
| awaiter_(std::move(other.awaiter_)) |
| { |
| } |
| |
| ~awaiter_task() |
| { |
| if (awaiter_) |
| (post)(destroy_awaiter<Executor>(ex_, std::move(awaiter_))); |
| } |
| |
| executor_type get_executor() const noexcept |
| { |
| return ex_; |
| } |
| |
| protected: |
| Executor ex_; |
| awaiter_ptr awaiter_; |
| }; |
| |
| template <typename Executor> |
| class spawn_handler : public awaiter_task<Executor> |
| { |
| public: |
| using awaiter_task::awaiter_task; |
| |
| void operator()() |
| { |
| awaiter_ptr ptr(std::move(awaiter_)); |
| coroutine_handle<awaiter>::from_promise(ptr.get()).resume(); |
| } |
| }; |
| |
| template <typename Executor, typename T> |
| class await_handler_base : public awaiter_task<Executor> |
| { |
| public: |
| typedef awaitable<T> awaitable_type; |
| |
| await_handler_base(basic_unsynchronized_await_context<Executor> ctx) |
| : awaiter_task<Executor>(ctx.get_executor(), ctx.awaiter_), |
| awaitee_(nullptr) |
| { |
| } |
| |
| awaitable<T> make_awaitable() |
| { |
| awaitable<T> a(make_dummy_awaitable<T>()); |
| awaitee_ = a.awaitee_; |
| return a; |
| } |
| |
| protected: |
| awaitee<T>* awaitee_; |
| }; |
| |
| template <typename Executor, typename... Args> |
| class await_handler; |
| |
| template <typename Executor> |
| class await_handler<Executor> |
| : public await_handler_base<Executor, void> |
| { |
| public: |
| using await_handler_base::await_handler_base; |
| |
| void operator()() |
| { |
| awaiter_ptr ptr(std::move(awaiter_)); |
| awaitee_->return_void(); |
| awaitee_->wake_caller(); |
| ptr->rethrow_exception(); |
| } |
| }; |
| |
| template <typename Executor> |
| class await_handler<Executor, error_code> |
| : public await_handler_base<Executor, void> |
| { |
| public: |
| typedef void return_type; |
| |
| using await_handler_base::await_handler_base; |
| |
| void operator()(error_code ec) |
| { |
| awaiter_ptr ptr(std::move(awaiter_)); |
| if (ec) |
| awaitee_->set_exception(std::make_exception_ptr(system_error(ec))); |
| else |
| awaitee_->return_void(); |
| awaitee_->wake_caller(); |
| ptr->rethrow_exception(); |
| } |
| }; |
| |
| template <typename Executor> |
| class await_handler<Executor, std::exception_ptr> |
| : public await_handler_base<Executor, void> |
| { |
| public: |
| using await_handler_base::await_handler_base; |
| |
| void operator()(std::exception_ptr ex) |
| { |
| awaiter_ptr ptr(std::move(awaiter_)); |
| if (ex) |
| awaitee_->set_exception(ex); |
| else |
| awaitee_->return_void(); |
| awaitee_->wake_caller(); |
| ptr->rethrow_exception(); |
| } |
| }; |
| |
| template <typename Executor, typename T> |
| class await_handler<Executor, T> |
| : public await_handler_base<Executor, T> |
| { |
| public: |
| using await_handler_base::await_handler_base; |
| |
| void operator()(T t) |
| { |
| awaiter_ptr ptr(std::move(awaiter_)); |
| awaitee_->return_value(std::forward<T>(t)); |
| awaitee_->wake_caller(); |
| ptr->rethrow_exception(); |
| } |
| }; |
| |
| template <typename Executor, typename T> |
| class await_handler<Executor, error_code, T> |
| : public await_handler_base<Executor, T> |
| { |
| public: |
| using await_handler_base::await_handler_base; |
| |
| void operator()(error_code ec, T t) |
| { |
| awaiter_ptr ptr(std::move(awaiter_)); |
| if (ec) |
| awaitee_->set_exception(std::make_exception_ptr(system_error(ec))); |
| else |
| awaitee_->return_value(std::forward<T>(t)); |
| awaitee_->wake_caller(); |
| ptr->rethrow_exception(); |
| } |
| }; |
| |
| template <typename Executor, typename T> |
| class await_handler<Executor, std::exception_ptr, T> |
| : public await_handler_base<Executor, T> |
| { |
| public: |
| using await_handler_base::await_handler_base; |
| |
| void operator()(std::exception_ptr ex, T t) |
| { |
| awaiter_ptr ptr(std::move(awaiter_)); |
| if (ex) |
| awaitee_->set_exception(ex); |
| else |
| awaitee_->return_value(std::forward<T>(t)); |
| awaitee_->wake_caller(); |
| ptr->rethrow_exception(); |
| } |
| }; |
| |
| template <typename Executor> |
| class make_await_context |
| { |
| public: |
| explicit make_await_context(Executor ex) |
| : ex_(ex) |
| { |
| } |
| |
| bool await_ready() |
| { |
| return false; |
| } |
| |
| void await_suspend(coroutine_handle<detail::awaiter> h) |
| { |
| awaiter_ = &h.promise(); |
| } |
| |
| basic_unsynchronized_await_context<Executor> await_resume() |
| { |
| return basic_unsynchronized_await_context<Executor>(ex_, awaiter_); |
| } |
| |
| private: |
| Executor ex_; |
| awaiter* awaiter_ = nullptr; |
| }; |
| |
| template <typename T> |
| struct awaitable_signature; |
| |
| template <typename T> |
| struct awaitable_signature<awaitable<T>> |
| { |
| typedef void type(std::exception_ptr, T); |
| }; |
| |
| template <> |
| struct awaitable_signature<awaitable<void>> |
| { |
| typedef void type(std::exception_ptr); |
| }; |
| |
| template <typename T, typename WorkGuard, typename Handler, |
| typename Executor, typename F, typename... Args> |
| awaiter* spawn_entry_point(awaitable<T>*, WorkGuard work_guard, |
| Handler handler, Executor ex, F f, Args... args) |
| { |
| bool done = false; |
| |
| try |
| { |
| T t = co_await std::invoke(std::move(f), std::move(args)..., |
| co_await make_await_context<Executor>(ex)); |
| |
| done = true; |
| |
| (dispatch)(work_guard.get_executor(), |
| [handler = std::move(handler), t = std::move(t)]() mutable |
| { |
| handler(std::exception_ptr(), std::move(t)); |
| }); |
| } |
| catch (...) |
| { |
| if (done) |
| throw; |
| |
| (dispatch)(work_guard.get_executor(), |
| [handler = std::move(handler), e = std::current_exception()]() mutable |
| { |
| handler(e, T()); |
| }); |
| } |
| } |
| |
| template <typename WorkGuard, typename Handler, |
| typename Executor, typename F, typename... Args> |
| awaiter* spawn_entry_point(awaitable<void>*, WorkGuard work_guard, |
| Handler handler, Executor ex, F f, Args... args) |
| { |
| std::exception_ptr e = nullptr; |
| |
| try |
| { |
| co_await std::invoke(std::move(f), std::move(args)..., |
| co_await make_await_context<Executor>(ex)); |
| } |
| catch (...) |
| { |
| e = std::current_exception(); |
| } |
| |
| (dispatch)(work_guard.get_executor(), |
| [handler = std::move(handler), e]() mutable |
| { |
| handler(e); |
| }); |
| } |
| |
| template <typename CompletionToken, |
| typename Executor, typename F, typename... Args> |
| auto spawn(CompletionToken&& token, const Executor& ex, F&& f, Args&&... args) |
| { |
| typedef decltype( |
| std::invoke(std::declval<std::decay_t<F>>(), |
| std::declval<std::decay_t<Args>>()..., |
| std::declval<basic_unsynchronized_await_context<Executor>>())) |
| awaitable_type; |
| |
| typedef typename awaitable_signature<awaitable_type>::type signature_type; |
| |
| async_completion<CompletionToken, signature_type> completion(token); |
| |
| auto work_guard = make_work_guard(completion.handler, ex); |
| awaiter* a = (spawn_entry_point)(static_cast<awaitable_type*>(nullptr), |
| std::move(work_guard), std::move(completion.handler), ex, |
| std::forward<F>(f), std::forward<Args>(args)...); |
| |
| (post)(spawn_handler<Executor>(ex, a)); |
| |
| return completion.result.get(); |
| } |
| |
| template <typename ArgTypes, typename Executor, |
| typename F, typename CompletionToken> |
| inline auto spawn_reorder(std::index_sequence<> index_seq, |
| const Executor& ex, F&& f, CompletionToken&& token) |
| { |
| return (spawn)(std::forward<CompletionToken>(token), ex, std::forward<F>(f)); |
| } |
| |
| template <typename ArgTypes, std::size_t... Index, |
| typename Executor, typename F, typename CompletionToken> |
| inline auto spawn_reorder(std::index_sequence<Index...> index_seq, |
| const Executor& ex, F&& f, std::tuple_element_t<Index, ArgTypes>&&... args, |
| CompletionToken&& token) |
| { |
| return (spawn)(std::forward<CompletionToken>(token), ex, std::move(f), |
| std::forward<std::tuple_element_t<Index, ArgTypes>>(args)...); |
| } |
| |
| } // namespace detail |
| |
| template <typename T> |
| awaitable<T>::~awaitable() |
| { |
| if (awaitee_) |
| { |
| detail::coroutine_handle< |
| detail::awaitee<T>>::from_promise( |
| awaitee_).destroy(); |
| } |
| } |
| |
| template <typename T> |
| inline bool awaitable<T>::await_ready() |
| { |
| return awaitee_->ready_; |
| } |
| |
| template <typename T> |
| inline void awaitable<T>::await_suspend( |
| detail::coroutine_handle<detail::awaiter> h) |
| { |
| awaitee_->caller_ = h; |
| } |
| |
| template <typename T> template <typename U> |
| inline void awaitable<T>::await_suspend( |
| detail::coroutine_handle<detail::awaitee<U>> h) |
| { |
| awaitee_->caller_ = h; |
| } |
| |
| template <typename T> |
| inline T awaitable<T>::await_resume() |
| { |
| awaitee_->caller_ = nullptr; |
| return awaitee_->value(); |
| } |
| |
| template <typename Executor, typename R, typename... Args> |
| struct handler_type<basic_unsynchronized_await_context<Executor>, R(Args...)> |
| { |
| typedef detail::await_handler< |
| Executor, typename decay<Args>::type...> type; |
| }; |
| |
| template <typename Executor, typename... Args> |
| class async_result<detail::await_handler<Executor, Args...>> |
| { |
| public: |
| typedef typename detail::await_handler< |
| Executor, Args...>::awaitable_type type; |
| |
| async_result(detail::await_handler<Executor, Args...>& h) |
| : awaitable_(h.make_awaitable()) |
| { |
| } |
| |
| type get() |
| { |
| return std::move(awaitable_); |
| } |
| |
| private: |
| type awaitable_; |
| }; |
| |
| template <typename Executor, typename F, typename... Args, typename> |
| inline auto spawn(const Executor& ex, F&& f, Args&&... args) |
| { |
| static_assert(sizeof...(Args) > 0, "CompletionToken required"); |
| |
| return detail::spawn_reorder<std::tuple<Args...>>( |
| std::make_index_sequence<sizeof...(Args) - 1>(), |
| ex, std::forward<F>(f), std::forward<Args>(args)...); |
| } |
| |
| template <typename ExecutionContext, typename F, typename... Args, typename> |
| inline auto spawn(ExecutionContext& ctx, F&& f, Args&&... args) |
| { |
| return (spawn)(ctx.get_executor(), std::forward<F>(f), |
| std::forward<Args>(args)...); |
| } |
| |
| template <typename Executor, typename F, typename... Args> |
| inline auto spawn(const basic_unsynchronized_await_context<Executor>& ctx, |
| F&& f, Args&&... args) |
| { |
| return (spawn)(ctx.get_executor(), std::forward<F>(f), |
| std::forward<Args>(args)...); |
| } |
| |
| } // namespace asio |
| |
| namespace std { |
| namespace experimental { |
| |
| template <typename... Args> |
| struct coroutine_traits<asio::detail::awaiter*, Args...> |
| { |
| typedef asio::detail::awaiter promise_type; |
| }; |
| |
| template <typename T, typename... Args> |
| struct coroutine_traits<asio::awaitable<T>, Args...> |
| { |
| typedef asio::detail::awaitee<T> promise_type; |
| }; |
| |
| } // namespace experimental |
| } // namespace std |
| |
| #include "asio/detail/pop_options.hpp" |
| |
| #endif // ASIO_IMPL_AWAIT_HPP |