123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292 |
- // Copyright (c) 2022 Klemens D. Morgenstern
- //
- // 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_COBALT_HANDLER_HPP
- #define BOOST_COBALT_HANDLER_HPP
- #include <boost/cobalt/this_coro.hpp>
- #include <boost/cobalt/unique_handle.hpp>
- #include <boost/cobalt/detail/util.hpp>
- #include <boost/cobalt/detail/sbo_resource.hpp>
- #include <boost/asio/bind_allocator.hpp>
- #include <boost/asio/post.hpp>
- #include <boost/system/result.hpp>
- #include <memory>
- #include <optional>
- namespace boost::cobalt
- {
- namespace detail
- {
- enum class completed_immediately_t
- {
- no, maybe, yes, initiating
- };
- struct completion_handler_noop_executor
- {
- executor exec;
- completed_immediately_t * completed_immediately = nullptr;
- template<typename Fn>
- void execute(Fn && fn) const
- {
- // only allow it when we're still initializing
- if (completed_immediately &&
- ((*completed_immediately == completed_immediately_t::initiating)
- || (*completed_immediately == completed_immediately_t::maybe)))
- {
- // only use this indicator if the fn will actually call our completion-handler
- // otherwise this was a single op in a composed operation
- *completed_immediately = completed_immediately_t::maybe;
- fn();
- // yes means completion_handler::operator() was called, so we're good.
- if (*completed_immediately != completed_immediately_t::yes)
- *completed_immediately = completed_immediately_t::initiating;
- }
- else
- {
- asio::post(exec, std::forward<Fn>(fn));
- }
- }
- friend bool operator==(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
- {
- return true;
- }
- friend bool operator!=(const completion_handler_noop_executor&, const completion_handler_noop_executor&) noexcept
- {
- return false;
- }
- completion_handler_noop_executor(const completion_handler_noop_executor & rhs) noexcept = default;
- completion_handler_noop_executor(cobalt::executor inner, completed_immediately_t * completed_immediately)
- : exec(std::move(inner)), completed_immediately(completed_immediately)
- {
- }
- };
- struct completion_handler_base
- {
- using cancellation_slot_type = asio::cancellation_slot;
- cancellation_slot_type cancellation_slot ;
- cancellation_slot_type get_cancellation_slot() const noexcept
- {
- return cancellation_slot ;
- }
- using executor_type = executor;
- const executor_type & executor_ ;
- const executor_type & get_executor() const noexcept
- {
- return executor_ ;
- }
- #if !defined(BOOST_COBALT_NO_PMR)
- using allocator_type = pmr::polymorphic_allocator<void>;
- pmr::polymorphic_allocator<void> allocator ;
- allocator_type get_allocator() const noexcept
- {
- return allocator ;
- }
- #else
- using allocator_type = detail::sbo_allocator<void>;
- detail::sbo_allocator<void> allocator ;
- allocator_type get_allocator() const noexcept
- {
- return allocator ;
- }
- #endif
- using immediate_executor_type = completion_handler_noop_executor;
- completed_immediately_t * completed_immediately = nullptr;
- immediate_executor_type get_immediate_executor() const noexcept
- {
- return {get_executor(), completed_immediately};
- }
- template<typename Promise>
- requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
- completion_handler_base(std::coroutine_handle<Promise> h,
- completed_immediately_t * completed_immediately = nullptr)
- : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
- executor_(h.promise().get_executor()),
- #if !defined(BOOST_COBALT_NO_PMR)
- allocator(asio::get_associated_allocator(h.promise(), this_thread::get_allocator())),
- #else
- allocator(detail::get_null_sbo_resource()),
- #endif
- completed_immediately(completed_immediately)
- {
- }
- #if !defined(BOOST_COBALT_NO_PMR)
- template<typename Promise>
- requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
- completion_handler_base(std::coroutine_handle<Promise> h,
- pmr::memory_resource * resource,
- completed_immediately_t * completed_immediately = nullptr)
- : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
- executor_(h.promise().get_executor()),
- allocator(resource),
- completed_immediately(completed_immediately)
- {
- }
- #else
- template<typename Promise>
- requires (requires (Promise p) {{p.get_executor()} -> std::same_as<const executor&>;})
- completion_handler_base(std::coroutine_handle<Promise> h,
- detail::sbo_resource * resource,
- completed_immediately_t * completed_immediately = nullptr)
- : cancellation_slot(asio::get_associated_cancellation_slot(h.promise())),
- executor_(h.promise().get_executor()),
- allocator(resource),
- completed_immediately(completed_immediately)
- {
- }
- #endif
- };
- template<typename Handler>
- void assign_cancellation(std::coroutine_handle<void>, Handler &&) {}
- template<typename Promise, typename Handler>
- void assign_cancellation(std::coroutine_handle<Promise> h, Handler && func)
- {
- if constexpr (requires {h.promise().get_cancellation_slot();})
- if (h.promise().get_cancellation_slot().is_connected())
- h.promise().get_cancellation_slot().assign(std::forward<Handler>(func));
- }
- template<typename Promise>
- const executor &
- get_executor(std::coroutine_handle<Promise> h)
- {
- if constexpr (requires {h.promise().get_executor();})
- {
- static_assert(std::same_as<decltype(h.promise().get_executor()),
- const executor &>,
- "for performance reasons, the get_executor function on a promise must return a const reference");
- return h.promise().get_executor();
- }
- else
- return this_thread::get_executor();
- }
- inline const executor &
- get_executor(std::coroutine_handle<>)
- {
- return this_thread::get_executor();
- }
- }
- template<typename ... Args>
- struct handler
- {
- void operator()(Args ... args)
- {
- result.emplace(static_cast<Args>(args)...);
- }
- handler(std::optional<std::tuple<Args...>> &result) : result(result) {}
- private:
- std::optional<std::tuple<Args...>> &result;
- };
- template<typename ... Args>
- handler(std::optional<std::tuple<Args...>> &result) -> handler<Args...>;
- template<typename ... Args>
- struct completion_handler : detail::completion_handler_base
- {
- completion_handler(completion_handler && ) = default;
- template<typename Promise>
- completion_handler(std::coroutine_handle<Promise> h,
- std::optional<std::tuple<Args...>> &result,
- detail::completed_immediately_t * completed_immediately = nullptr
- #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
- , const boost::source_location & loc = BOOST_CURRENT_LOCATION
- #endif
- ) : completion_handler_base(h, completed_immediately),
- self(h.address()), result(result)
- #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
- , loc_(loc)
- #endif
- {
- }
- #if !defined(BOOST_COBALT_NO_PMR)
- template<typename Promise>
- completion_handler(std::coroutine_handle<Promise> h,
- std::optional<std::tuple<Args...>> &result,
- pmr::memory_resource * resource,
- detail::completed_immediately_t * completed_immediately = nullptr
- #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
- , const boost::source_location & loc = BOOST_CURRENT_LOCATION
- #endif
- ) : completion_handler_base(h, resource, completed_immediately),
- self(h.address()), result(result)
- #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
- , loc_(loc)
- #endif
- {
- }
- #else
- template<typename Promise>
- completion_handler(std::coroutine_handle<Promise> h,
- std::optional<std::tuple<Args...>> &result,
- detail::sbo_resource * resource,
- detail::completed_immediately_t * completed_immediately = nullptr)
- : completion_handler_base(h, resource, completed_immediately),
- self(h.address()), result(result)
- {
- }
- #endif
- void operator()(Args ... args)
- {
- #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
- BOOST_ASIO_HANDLER_LOCATION((loc_.file_name(), loc_.line(), loc_.function_name()));
- #endif
- result.emplace(std::move(args)...);
- BOOST_ASSERT(this->self != nullptr);
- auto p = this->self.release();
- if (completed_immediately != nullptr
- && *completed_immediately == detail::completed_immediately_t::maybe)
- {
- *completed_immediately = detail::completed_immediately_t::yes;
- return;
- }
- std::move(p)();
- }
- using result_type = std::optional<std::tuple<Args...>>;
- ~completion_handler()
- {
- if (self && completed_immediately
- && *completed_immediately == detail::completed_immediately_t::initiating
- && std::uncaught_exceptions() > 0)
- self.release();
- }
- private:
- unique_handle<void> self;
- std::optional<std::tuple<Args...>> &result;
- #if defined(BOOST_ASIO_ENABLE_HANDLER_TRACKING)
- boost::source_location loc_;
- #endif
- };
- };
- #endif //BOOST_COBALT_HANDLER_HPP
|