diff options
Diffstat (limited to '3rdparty/asio/include/asio/experimental/promise.hpp')
-rw-r--r-- | 3rdparty/asio/include/asio/experimental/promise.hpp | 606 |
1 files changed, 606 insertions, 0 deletions
diff --git a/3rdparty/asio/include/asio/experimental/promise.hpp b/3rdparty/asio/include/asio/experimental/promise.hpp new file mode 100644 index 00000000000..89ca9c1b496 --- /dev/null +++ b/3rdparty/asio/include/asio/experimental/promise.hpp @@ -0,0 +1,606 @@ +// +// experimental/promise.hpp +// ~~~~~~~~~~~~~~~~~~~~~~~~ +// +// Copyright (c) 2021 Klemens D. Morgenstern +// (klemens dot morgenstern at gmx dot net) +// +// 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_EXPERIMENTAL_PROMISE_HPP +#define ASIO_EXPERIMENTAL_PROMISE_HPP + +#include "asio/detail/config.hpp" +#include "asio/detail/type_traits.hpp" +#include "asio/any_io_executor.hpp" +#include "asio/associated_cancellation_slot.hpp" +#include "asio/bind_executor.hpp" +#include "asio/cancellation_signal.hpp" +#include "asio/experimental/detail/completion_handler_erasure.hpp" +#include "asio/experimental/impl/promise.hpp" +#include "asio/post.hpp" + +#include <variant> + +#include "asio/detail/push_options.hpp" + +namespace asio { +namespace experimental { + +template <typename Executor = any_io_executor> +struct use_promise_t {}; + +constexpr use_promise_t<> use_promise; + +template <typename T> +struct is_promise : std::false_type {}; + +template <typename ... Ts> +struct is_promise<promise<Ts...>> : std::true_type {}; + +template <typename T> +constexpr bool is_promise_v = is_promise<T>::value; + +template <typename T> +concept is_promise_c = is_promise_v<std::remove_reference_t<T>>; + +template <typename ... Ts> +struct promise_value_type +{ + using type = std::tuple<Ts...>; +}; + +template <typename T> +struct promise_value_type<T> +{ + using type = T; +}; + +template <> +struct promise_value_type<> +{ + using type = std::monostate; +}; + +#if defined(GENERATING_DOCUMENTATION) +/// The primary template is not defined. +template<typename Signature = void(), typename Executor = any_io_executor> +struct promise +{ +}; +#endif // defined(GENERATING_DOCUMENTATION) + +template <typename ... Ts, typename Executor> +struct promise<void(Ts...), Executor> +{ + using value_type = typename promise_value_type<Ts...>::type; + using tuple_type = std::tuple<Ts...>; + using executor_type = Executor; + + executor_type get_executor() const + { + if (impl_) + return impl_->executor; + else + return {}; + } + + void cancel(cancellation_type level = cancellation_type::all) + { + if (impl_ && !impl_->done) + { + asio::dispatch(impl_->executor, + [level, impl = impl_]{impl->cancel.emit(level);}); + } + } + + bool complete() const noexcept + { + return impl_ && impl_->done; + } + + template <typename CompletionToken> + auto async_wait(CompletionToken&& token) + { + assert(impl_); + + return async_initiate<CompletionToken, void(Ts...)>( + initiate_async_wait{impl_}, token); + } + + promise() = delete; + promise(const promise& ) = delete; + promise(promise&& ) noexcept = default; + + ~promise() { cancel(); } + + template <execution::executor Executor1, is_promise_c ... Ps> + static auto race(Executor1 exec, Ps ... ps) + -> promise<void(std::variant<typename Ps::value_type...>), Executor1> + { + using var_t = std::variant<typename Ps::value_type...>; + using pi = detail::promise_impl<void(var_t), Executor1>; + + struct impl_t : pi + { + impl_t(Executor1 exec, Ps&& ... ps) + : pi(std::move(exec)), + tup(std::move(ps)...) + { + this->slot.template emplace<cancel_handler>(this); + } + + struct cancel_handler + { + impl_t* self; + + cancel_handler(impl_t* self) + : self(self) + { + } + + void operator()(cancellation_type ct) + { + [ct, s=self]<std::size_t... Idx>(std::index_sequence<Idx...>) + { + (std::get<Idx>(s->tup).cancel(ct), ... ); + }(std::make_index_sequence<sizeof...(Ps)>{}); + } + }; + + std::tuple<std::remove_reference_t<Ps>...> tup; + cancellation_slot slot{this->cancel.slot()}; + }; + + auto impl = std::allocate_shared<impl_t>( + get_associated_allocator(exec), exec, std::move(ps)...); + + impl->executor = exec; + + [impl, exec]<std::size_t... Idx>(std::index_sequence<Idx...>) + { + auto step = + [&]<std::size_t I>(std::integral_constant<std::size_t, I>) + { + return [impl]<typename... Args > (Args&& ... args) + { + if (impl->done) + return; + impl->result = var_t(std::in_place_index<I>, + std::forward<Args>(args)...); + impl->done = true; + if (auto f = std::exchange(impl->completion, nullptr); !!f) + std::apply(std::move(f), std::move(*impl->result)); + + auto cancel = + [&]<std::size_t Id>(std::integral_constant<std::size_t, Id>) + { + if constexpr (I != Id) + get<I>(impl->tup).cancel(); + }; + + (cancel(std::integral_constant<std::size_t, Idx>{}), ...); + }; + }; + + ( + std::get<Idx>(impl->tup).async_wait( + bind_executor(exec, + step(std::integral_constant<std::size_t, Idx>{}))), + ... + ); + }(std::make_index_sequence<sizeof...(Ps)>{}); + + return {impl}; + } + + template <execution::executor Executor1, is_promise_c ... Ps> + static auto all(Executor1 exec, Ps ... ps) + -> promise<void(typename Ps::value_type...), Executor1> + { + using pi = detail::promise_impl< + void(typename Ps::value_type...), Executor1>; + + struct impl_t : pi + { + impl_t(Executor1 exec, Ps&& ... ps) + : pi(std::move(exec)), + tup(std::move(ps)...) + { + this->slot.template emplace<cancel_handler>(this); + } + + struct cancel_handler + { + impl_t* self; + + cancel_handler(impl_t* self) + : self(self) + { + } + + void operator()(cancellation_type level) + { + [level, s=self]<std::size_t... Idx>(std::index_sequence<Idx...>) + { + (std::get<Idx>(s->tup).cancel(level), ... ); + }(std::make_index_sequence<sizeof...(Ps)>{}); + } + }; + + std::tuple<std::remove_reference_t<Ps>...> tup; + std::tuple<std::optional<typename Ps::value_type>...> partial_result; + cancellation_slot slot{this->cancel.slot()}; + }; + + auto impl = std::allocate_shared<impl_t>( + get_associated_allocator(exec), exec, std::move(ps)...); + impl->executor = exec; + + [impl, exec]<std::size_t... Idx>(std::index_sequence<Idx...>) + { + auto step = + [&]<std::size_t I>(std::integral_constant<std::size_t, I>) + { + return [impl]<typename... Args>(Args&& ... args) + { + std::get<I>(impl->partial_result).emplace( + std::forward<Args>(args)...); + if ((std::get<Idx>(impl->partial_result) && ...)) // we're done. + { + impl->result = {*std::get<Idx>(impl->partial_result)...}; + + impl->done = true; + if (auto f = std::exchange(impl->completion, nullptr); !!f) + std::apply(std::move(f), std::move(*impl->result)); + } + }; + }; + + ( + std::get<Idx>(impl->tup).async_wait( + bind_executor(exec, + step(std::integral_constant<std::size_t, Idx>{}))), + ... + ); + }(std::make_index_sequence<sizeof...(Ps)>{}); + + return {impl}; + } + + template <is_promise_c ... Ps> + static auto race(Ps ... ps) + { + auto exec = get<0>(std::tie(ps...)).get_executor(); + return race(std::move(exec), std::move(ps)...); + } + + template <is_promise_c ... Ps> + static auto all(Ps ... ps) + { + auto exec = get<0>(std::tie(ps...)).get_executor(); + return all(std::move(exec), std::move(ps)...); + } + + template <execution::executor Executor1, typename Range> +#if !defined(GENERATING_DOCUMENTATION) + requires requires (Range r) + { + {*std::begin(r)} -> is_promise_c; + {*std:: end(r)} -> is_promise_c; + } +#endif // !defined(GENERATING_DOCUMENTATION) + static auto race(Executor1 exec, Range range) + { + using var_t = typename std::decay_t< + decltype(*std::begin(range))>::value_type; + using signature_type = std::conditional_t< + std::is_same_v<var_t, std::monostate>, + void(std::size_t), + void(std::size_t, var_t)>; + using pi = detail::promise_impl<signature_type, Executor1>; + using promise_t = promise<signature_type, Executor1>; + + struct impl_t : pi + { + impl_t(Executor1 exec, Range&& range) + : pi(std::move(exec)), + range(std::move(range)) + { + this->slot.template emplace<cancel_handler>(this); + } + + struct cancel_handler + { + impl_t* self; + + cancel_handler(impl_t* self) + : self(self) + { + } + + void operator()(asio::cancellation_type ct) + { + for (auto& r : self->range) + r.cancel(ct); + } + }; + + Range range; + cancellation_slot slot{this->cancel.slot()}; + }; + + const auto size = std::distance(std::begin(range), std::end(range)); + auto impl = std::allocate_shared<impl_t>( + get_associated_allocator(exec), exec, std::move(range)); + impl->executor = exec; + + if (size == 0u) + { + if constexpr (std::is_same_v<var_t, std::monostate>) + impl->result = {-1}; + else + impl->result = {-1, var_t{}}; + + impl->done = true; + if (auto f = std::exchange(impl->completion, nullptr); !!f) + { + asio::post(exec, + [impl, f = std::move(f)]() mutable + { + std::apply(std::move(f), std::move(*impl->result)); + }); + } + return promise_t{impl}; + } + auto idx = 0u; + for (auto& val : impl->range) + { + val.async_wait( + bind_executor(exec, + [idx, impl]<typename... Args>(Args&&... args) + { + if (impl->done) + return; + if constexpr (std::is_same_v<var_t, std::monostate>) + impl->result = idx; + else + impl->result = std::make_tuple(idx, + var_t(std::forward<Args>(args)...)); + impl->done = true; + if (auto f = std::exchange(impl->completion, nullptr); !!f) + std::apply(std::move(f), std::move(*impl->result)); + + auto jdx = 0u; + + for (auto &tc : impl->range) + if (jdx++ != idx) + tc.cancel(); + })); + idx++; + } + return promise_t{impl}; + } + + + template <execution::executor Executor1, typename Range> +#if !defined(GENERATING_DOCUMENTATION) + requires requires (Range r) + { + {*std::begin(r)} -> is_promise_c; + {*std:: end(r)} -> is_promise_c; + } +#endif // !defined(GENERATING_DOCUMENTATION) + static auto all(Executor1 exec, Range range) + -> promise< + void( + std::vector< + typename std::decay_t< + decltype(*std::begin(range)) + >::value_type + > + ), Executor1> + { + using var_t = typename std::decay_t< + decltype(*std::begin(range))>::value_type; + using pi = detail::promise_impl<void(std::vector<var_t>), Executor1>; + + struct impl_t : pi + { + impl_t(Executor1 exec, Range&& range) + : pi(std::move(exec)), + range(std::move(range)) + { + this->slot.template emplace<cancel_handler>(this); + } + + struct cancel_handler + { + impl_t* self; + + cancel_handler(impl_t* self) + : self(self) + { + } + + void operator()(cancellation_type ct) + { + for (auto& r : self->range) + r.cancel(ct); + } + }; + + Range range; + std::vector<std::optional<var_t>> partial_result; + cancellation_slot slot{this->cancel.slot()}; + }; + + const auto size = std::distance(std::begin(range), std::end(range)); + auto impl = std::allocate_shared<impl_t>( + get_associated_allocator(exec), exec, std::move(range)); + impl->executor = exec; + impl->partial_result.resize(size); + + if (size == 0u) + { + impl->result.emplace(); + impl->done = true; + if (auto f = std::exchange(impl->completion, nullptr); !!f) + asio::post(exec, [impl, f = std::move(f)]() mutable + { + std::apply(std::move(f), std::move(*impl->result)); + }); + return {impl}; + } + auto idx = 0u; + for (auto& val : impl->range) { + val.async_wait(bind_executor( + exec, + [idx, impl]<typename... Args>(Args&&... args) { + + impl->partial_result[idx].emplace(std::forward<Args>(args)...); + if (std::all_of(impl->partial_result.begin(), + impl->partial_result.end(), + [](auto &opt) {return opt.has_value();})) + { + impl->result.emplace(); + get<0>(*impl->result).reserve(impl->partial_result.size()); + for (auto& p : impl->partial_result) + get<0>(*impl->result).push_back(std::move(*p)); + + impl->done = true; + if (auto f = std::exchange(impl->completion, nullptr); !!f) + std::apply(std::move(f), std::move(*impl->result)); + } + + })); + idx++; + } + return {impl}; + } + + template <typename Range> +#if !defined(GENERATING_DOCUMENTATION) + requires requires (Range r) + { + {*std::begin(r)} -> is_promise_c; + {*std:: end(r)} -> is_promise_c; + } +#endif // !defined(GENERATING_DOCUMENTATION) + static auto race(Range range) + { + if (std::begin(range) == std::end(range)) + throw std::logic_error( + "Can't use race on an empty range with deduced executor"); + else + return race(std::begin(range)->get_executor(), std::move(range)); + } + + template <typename Range> +#if !defined(GENERATING_DOCUMENTATION) + requires requires (Range&& r) + { + {*std::begin(r)} -> is_promise_c; + {*std:: end(r)} -> is_promise_c; + } +#endif // !defined(GENERATING_DOCUMENTATION) + static auto all(Range range) + { + if (std::begin(range) == std::end(range)) + throw std::logic_error( + "Can't use all on an empty range with deduced executor"); + else + return all(std::begin(range)->get_executor(), std::move(range)); + } + +private: +#if !defined(GENERATING_DOCUMENTATION) + template <typename, typename> friend struct promise; + friend struct detail::promise_handler<void(Ts...)>; +#endif // !defined(GENERATING_DOCUMENTATION) + + std::shared_ptr<detail::promise_impl<void(Ts...), Executor>> impl_; + promise(std::shared_ptr<detail::promise_impl<void(Ts...), Executor>> impl) + : impl_(impl) + { + } + + struct initiate_async_wait + { + std::shared_ptr<detail::promise_impl<void(Ts...), Executor>> self_; + + template <typename WaitHandler> + void operator()(WaitHandler&& handler) const + { + const auto exec = get_associated_executor(handler, self_->executor); + auto cancel = get_associated_cancellation_slot(handler); + if (self_->done) + { + asio::post(exec, + [self = self_, h = std::forward<WaitHandler>(handler)]() mutable + { + std::apply(std::forward<WaitHandler>(h), + std::move(*self->result)); + }); + } + else + { + if (cancel.is_connected()) + { + struct cancel_handler + { + std::weak_ptr<detail::promise_impl<void(Ts...), Executor>> self; + + cancel_handler( + std::weak_ptr<detail::promise_impl<void(Ts...), Executor>> self) + : self(std::move(self)) + { + } + + void operator()(cancellation_type level) const + { + if (auto p = self.lock(); p != nullptr) + p->cancel.emit(level); + + } + }; + + cancel.template emplace<cancel_handler>(self_); + } + + self_->completion = {exec, std::forward<WaitHandler>(handler)}; + } + } + }; +}; + +} // namespace experimental + +#if !defined(GENERATING_DOCUMENTATION) + +template <typename Executor, typename R, typename... Args> +struct async_result<experimental::use_promise_t<Executor>, R(Args...)> +{ + using handler_type = experimental::detail::promise_handler< + void(typename decay<Args>::type...), Executor>; + + template <typename Initiation, typename... InitArgs> + static auto initiate(Initiation initiation, + experimental::use_promise_t<Executor>, InitArgs... args) + -> typename handler_type::promise_type + { + handler_type ht{get_associated_executor(initiation)}; + std::move(initiation)(ht, std::move(args)...); + return ht.make_promise(); + } +}; + +#endif // !defined(GENERATING_DOCUMENTATION) + +} // namespace asio + +#include "asio/detail/pop_options.hpp" + +#endif // ASIO_EXPERIMENTAL_PROMISE_HPP |