1176 lines
34 KiB
C++
1176 lines
34 KiB
C++
//
|
|
// experimental/impl/co_composed.hpp
|
|
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
// Copyright (c) 2003-2023 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_EXPERIMENTAL_CO_COMPOSED_HPP
|
|
#define ASIO_IMPL_EXPERIMENTAL_CO_COMPOSED_HPP
|
|
|
|
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
|
|
# pragma once
|
|
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
|
|
|
|
#include "asio/detail/config.hpp"
|
|
#include <new>
|
|
#include <tuple>
|
|
#include <variant>
|
|
#include "asio/associated_cancellation_slot.hpp"
|
|
#include "asio/associator.hpp"
|
|
#include "asio/async_result.hpp"
|
|
#include "asio/cancellation_state.hpp"
|
|
#include "asio/detail/composed_work.hpp"
|
|
#include "asio/detail/recycling_allocator.hpp"
|
|
#include "asio/detail/throw_error.hpp"
|
|
#include "asio/detail/type_traits.hpp"
|
|
#include "asio/error.hpp"
|
|
|
|
#if defined(ASIO_HAS_STD_COROUTINE)
|
|
# include <coroutine>
|
|
#else // defined(ASIO_HAS_STD_COROUTINE)
|
|
# include <experimental/coroutine>
|
|
#endif // defined(ASIO_HAS_STD_COROUTINE)
|
|
|
|
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
# if defined(ASIO_HAS_SOURCE_LOCATION)
|
|
# include "asio/detail/source_location.hpp"
|
|
# endif // defined(ASIO_HAS_SOURCE_LOCATION)
|
|
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
|
|
#include "asio/detail/push_options.hpp"
|
|
|
|
namespace asio {
|
|
namespace experimental {
|
|
namespace detail {
|
|
|
|
#if defined(ASIO_HAS_STD_COROUTINE)
|
|
using std::coroutine_handle;
|
|
using std::suspend_always;
|
|
using std::suspend_never;
|
|
#else // defined(ASIO_HAS_STD_COROUTINE)
|
|
using std::experimental::coroutine_handle;
|
|
using std::experimental::suspend_always;
|
|
using std::experimental::suspend_never;
|
|
#endif // defined(ASIO_HAS_STD_COROUTINE)
|
|
|
|
using asio::detail::composed_io_executors;
|
|
using asio::detail::composed_work;
|
|
using asio::detail::composed_work_guard;
|
|
using asio::detail::get_composed_io_executor;
|
|
using asio::detail::make_composed_io_executors;
|
|
using asio::detail::recycling_allocator;
|
|
using asio::detail::throw_error;
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_state;
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_handler_base;
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_promise;
|
|
|
|
template <completion_signature... Signatures>
|
|
class co_composed_returns
|
|
{
|
|
};
|
|
|
|
struct co_composed_on_suspend
|
|
{
|
|
void (*fn_)(void*) = nullptr;
|
|
void* arg_ = nullptr;
|
|
};
|
|
|
|
template <typename... T>
|
|
struct co_composed_completion : std::tuple<T&&...>
|
|
{
|
|
template <typename... U>
|
|
co_composed_completion(U&&... u) noexcept
|
|
: std::tuple<T&&...>(std::forward<U>(u)...)
|
|
{
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename Signature>
|
|
class co_composed_state_return_overload;
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename R, typename... Args>
|
|
class co_composed_state_return_overload<
|
|
Executors, Handler, Return, R(Args...)>
|
|
{
|
|
public:
|
|
using derived_type = co_composed_state<Executors, Handler, Return>;
|
|
using promise_type = co_composed_promise<Executors, Handler, Return>;
|
|
using return_type = std::tuple<Args...>;
|
|
|
|
void on_cancellation_complete_with(Args... args)
|
|
{
|
|
derived_type& state = *static_cast<derived_type*>(this);
|
|
state.return_value_ = std::make_tuple(std::move(args)...);
|
|
state.cancellation_on_suspend_fn(
|
|
[](void* p)
|
|
{
|
|
auto& promise = *static_cast<promise_type*>(p);
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return> composed_handler(promise);
|
|
|
|
Handler handler(std::move(promise.state().handler_));
|
|
return_type result(
|
|
std::move(std::get<return_type>(promise.state().return_value_)));
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return>(std::move(composed_handler));
|
|
|
|
std::apply(std::move(handler), std::move(result));
|
|
});
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_state_return;
|
|
|
|
template <typename Executors, typename Handler, typename... Signatures>
|
|
class co_composed_state_return<
|
|
Executors, Handler, co_composed_returns<Signatures...>>
|
|
: public co_composed_state_return_overload<Executors,
|
|
Handler, co_composed_returns<Signatures...>, Signatures>...
|
|
{
|
|
public:
|
|
using co_composed_state_return_overload<Executors,
|
|
Handler, co_composed_returns<Signatures...>,
|
|
Signatures>::on_cancellation_complete_with...;
|
|
|
|
private:
|
|
template <typename, typename, typename, typename>
|
|
friend class co_composed_promise_return_overload;
|
|
template <typename, typename, typename, typename>
|
|
friend class co_composed_state_return_overload;
|
|
|
|
std::variant<std::monostate,
|
|
typename co_composed_state_return_overload<
|
|
Executors, Handler, co_composed_returns<Signatures...>,
|
|
Signatures>::return_type...> return_value_;
|
|
};
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename... Signatures>
|
|
struct co_composed_state_default_cancellation_on_suspend_impl;
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
struct co_composed_state_default_cancellation_on_suspend_impl<
|
|
Executors, Handler, Return>
|
|
{
|
|
static constexpr void (*fn())(void*)
|
|
{
|
|
return nullptr;
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return,
|
|
typename R, typename... Args, typename... Signatures>
|
|
struct co_composed_state_default_cancellation_on_suspend_impl<
|
|
Executors, Handler, Return, R(Args...), Signatures...>
|
|
{
|
|
static constexpr void (*fn())(void*)
|
|
{
|
|
return co_composed_state_default_cancellation_on_suspend_impl<
|
|
Executors, Handler, Return, Signatures...>::fn();
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return,
|
|
typename R, typename... Args, typename... Signatures>
|
|
struct co_composed_state_default_cancellation_on_suspend_impl<Executors,
|
|
Handler, Return, R(asio::error_code, Args...), Signatures...>
|
|
{
|
|
using promise_type = co_composed_promise<Executors, Handler, Return>;
|
|
using return_type = std::tuple<asio::error_code, Args...>;
|
|
|
|
static constexpr void (*fn())(void*)
|
|
{
|
|
if constexpr ((is_constructible<Args>::value && ...))
|
|
{
|
|
return [](void* p)
|
|
{
|
|
auto& promise = *static_cast<promise_type*>(p);
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return> composed_handler(promise);
|
|
|
|
Handler handler(std::move(promise.state().handler_));
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return>(std::move(composed_handler));
|
|
|
|
std::move(handler)(
|
|
asio::error_code(asio::error::operation_aborted),
|
|
Args{}...);
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return co_composed_state_default_cancellation_on_suspend_impl<
|
|
Executors, Handler, Return, Signatures...>::fn();
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return,
|
|
typename R, typename... Args, typename... Signatures>
|
|
struct co_composed_state_default_cancellation_on_suspend_impl<Executors,
|
|
Handler, Return, R(std::exception_ptr, Args...), Signatures...>
|
|
{
|
|
using promise_type = co_composed_promise<Executors, Handler, Return>;
|
|
using return_type = std::tuple<std::exception_ptr, Args...>;
|
|
|
|
static constexpr void (*fn())(void*)
|
|
{
|
|
if constexpr ((is_constructible<Args>::value && ...))
|
|
{
|
|
return [](void* p)
|
|
{
|
|
auto& promise = *static_cast<promise_type*>(p);
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return> composed_handler(promise);
|
|
|
|
Handler handler(std::move(promise.state().handler_));
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return>(std::move(composed_handler));
|
|
|
|
std::move(handler)(
|
|
std::make_exception_ptr(
|
|
asio::system_error(
|
|
asio::error::operation_aborted, "co_await")),
|
|
Args{}...);
|
|
};
|
|
}
|
|
else
|
|
{
|
|
return co_composed_state_default_cancellation_on_suspend_impl<
|
|
Executors, Handler, Return, Signatures...>::fn();
|
|
}
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
struct co_composed_state_default_cancellation_on_suspend;
|
|
|
|
template <typename Executors, typename Handler, typename... Signatures>
|
|
struct co_composed_state_default_cancellation_on_suspend<
|
|
Executors, Handler, co_composed_returns<Signatures...>>
|
|
: co_composed_state_default_cancellation_on_suspend_impl<Executors,
|
|
Handler, co_composed_returns<Signatures...>, Signatures...>
|
|
{
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_state_cancellation
|
|
{
|
|
public:
|
|
using cancellation_slot_type = cancellation_slot;
|
|
|
|
cancellation_slot_type get_cancellation_slot() const noexcept
|
|
{
|
|
return cancellation_state_.slot();
|
|
}
|
|
|
|
cancellation_state get_cancellation_state() const noexcept
|
|
{
|
|
return cancellation_state_;
|
|
}
|
|
|
|
void reset_cancellation_state()
|
|
{
|
|
cancellation_state_ = cancellation_state(
|
|
(get_associated_cancellation_slot)(
|
|
static_cast<co_composed_state<Executors, Handler, Return>*>(
|
|
this)->handler()));
|
|
}
|
|
|
|
template <typename Filter>
|
|
void reset_cancellation_state(Filter filter)
|
|
{
|
|
cancellation_state_ = cancellation_state(
|
|
(get_associated_cancellation_slot)(
|
|
static_cast<co_composed_state<Executors, Handler, Return>*>(
|
|
this)->handler()), filter, filter);
|
|
}
|
|
|
|
template <typename InFilter, typename OutFilter>
|
|
void reset_cancellation_state(InFilter&& in_filter, OutFilter&& out_filter)
|
|
{
|
|
cancellation_state_ = cancellation_state(
|
|
(get_associated_cancellation_slot)(
|
|
static_cast<co_composed_state<Executors, Handler, Return>*>(
|
|
this)->handler()),
|
|
std::forward<InFilter>(in_filter),
|
|
std::forward<OutFilter>(out_filter));
|
|
}
|
|
|
|
cancellation_type_t cancelled() const noexcept
|
|
{
|
|
return cancellation_state_.cancelled();
|
|
}
|
|
|
|
void clear_cancellation_slot() noexcept
|
|
{
|
|
cancellation_state_.slot().clear();
|
|
}
|
|
|
|
[[nodiscard]] bool throw_if_cancelled() const noexcept
|
|
{
|
|
return throw_if_cancelled_;
|
|
}
|
|
|
|
void throw_if_cancelled(bool b) noexcept
|
|
{
|
|
throw_if_cancelled_ = b;
|
|
}
|
|
|
|
[[nodiscard]] bool complete_if_cancelled() const noexcept
|
|
{
|
|
return complete_if_cancelled_;
|
|
}
|
|
|
|
void complete_if_cancelled(bool b) noexcept
|
|
{
|
|
complete_if_cancelled_ = b;
|
|
}
|
|
|
|
private:
|
|
template <typename, typename, typename>
|
|
friend class co_composed_promise;
|
|
template <typename, typename, typename, typename>
|
|
friend class co_composed_state_return_overload;
|
|
|
|
void cancellation_on_suspend_fn(void (*fn)(void*))
|
|
{
|
|
cancellation_on_suspend_fn_ = fn;
|
|
}
|
|
|
|
void check_for_cancellation_on_transform()
|
|
{
|
|
if (throw_if_cancelled_ && !!cancelled())
|
|
throw_error(asio::error::operation_aborted, "co_await");
|
|
}
|
|
|
|
bool check_for_cancellation_on_suspend(
|
|
co_composed_promise<Executors, Handler, Return>& promise) noexcept
|
|
{
|
|
if (complete_if_cancelled_ && !!cancelled() && cancellation_on_suspend_fn_)
|
|
{
|
|
promise.state().work_.reset();
|
|
promise.state().on_suspend_->fn_ = cancellation_on_suspend_fn_;
|
|
promise.state().on_suspend_->arg_ = &promise;
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
cancellation_state cancellation_state_;
|
|
void (*cancellation_on_suspend_fn_)(void*) =
|
|
co_composed_state_default_cancellation_on_suspend<
|
|
Executors, Handler, Return>::fn();
|
|
bool throw_if_cancelled_ = false;
|
|
bool complete_if_cancelled_ = true;
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
requires is_same<
|
|
typename associated_cancellation_slot<
|
|
Handler, cancellation_slot
|
|
>::asio_associated_cancellation_slot_is_unspecialised,
|
|
void>::value
|
|
class co_composed_state_cancellation<Executors, Handler, Return>
|
|
{
|
|
public:
|
|
void reset_cancellation_state()
|
|
{
|
|
}
|
|
|
|
template <typename Filter>
|
|
void reset_cancellation_state(Filter)
|
|
{
|
|
}
|
|
|
|
template <typename InFilter, typename OutFilter>
|
|
void reset_cancellation_state(InFilter&&, OutFilter&&)
|
|
{
|
|
}
|
|
|
|
cancellation_type_t cancelled() const noexcept
|
|
{
|
|
return cancellation_type::none;
|
|
}
|
|
|
|
void clear_cancellation_slot() noexcept
|
|
{
|
|
}
|
|
|
|
[[nodiscard]] bool throw_if_cancelled() const noexcept
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void throw_if_cancelled(bool) noexcept
|
|
{
|
|
}
|
|
|
|
[[nodiscard]] bool complete_if_cancelled() const noexcept
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void complete_if_cancelled(bool) noexcept
|
|
{
|
|
}
|
|
|
|
private:
|
|
template <typename, typename, typename>
|
|
friend class co_composed_promise;
|
|
template <typename, typename, typename, typename>
|
|
friend class co_composed_state_return_overload;
|
|
|
|
void cancellation_on_suspend_fn(void (*)(void*))
|
|
{
|
|
}
|
|
|
|
void check_for_cancellation_on_transform() noexcept
|
|
{
|
|
}
|
|
|
|
bool check_for_cancellation_on_suspend(
|
|
co_composed_promise<Executors, Handler, Return>&) noexcept
|
|
{
|
|
return true;
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_state
|
|
: public co_composed_state_return<Executors, Handler, Return>,
|
|
public co_composed_state_cancellation<Executors, Handler, Return>
|
|
{
|
|
public:
|
|
using io_executor_type = typename composed_work_guard<
|
|
typename composed_work<Executors>::head_type>::executor_type;
|
|
|
|
template <typename H>
|
|
co_composed_state(composed_io_executors<Executors>&& executors,
|
|
H&& h, co_composed_on_suspend& on_suspend)
|
|
: work_(std::move(executors)),
|
|
handler_(std::forward<H>(h)),
|
|
on_suspend_(&on_suspend)
|
|
{
|
|
this->reset_cancellation_state(enable_terminal_cancellation());
|
|
}
|
|
|
|
io_executor_type get_io_executor() const noexcept
|
|
{
|
|
return work_.head_.get_executor();
|
|
}
|
|
|
|
template <typename... Args>
|
|
[[nodiscard]] co_composed_completion<Args...> complete(Args&&... args)
|
|
requires requires { declval<Handler>()(std::forward<Args>(args)...); }
|
|
{
|
|
return co_composed_completion<Args...>(std::forward<Args>(args)...);
|
|
}
|
|
|
|
const Handler& handler() const noexcept
|
|
{
|
|
return handler_;
|
|
}
|
|
|
|
private:
|
|
template <typename, typename, typename>
|
|
friend class co_composed_handler_base;
|
|
template <typename, typename, typename>
|
|
friend class co_composed_promise;
|
|
template <typename, typename, typename, typename>
|
|
friend class co_composed_promise_return_overload;
|
|
template <typename, typename, typename>
|
|
friend class co_composed_state_cancellation;
|
|
template <typename, typename, typename, typename>
|
|
friend class co_composed_state_return_overload;
|
|
template <typename, typename, typename, typename...>
|
|
friend struct co_composed_state_default_cancellation_on_suspend_impl;
|
|
|
|
composed_work<Executors> work_;
|
|
Handler handler_;
|
|
co_composed_on_suspend* on_suspend_;
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_handler_cancellation
|
|
{
|
|
public:
|
|
using cancellation_slot_type = cancellation_slot;
|
|
|
|
cancellation_slot_type get_cancellation_slot() const noexcept
|
|
{
|
|
return static_cast<
|
|
const co_composed_handler_base<Executors, Handler, Return>*>(
|
|
this)->promise().state().get_cancellation_slot();
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
requires is_same<
|
|
typename associated_cancellation_slot<
|
|
Handler, cancellation_slot
|
|
>::asio_associated_cancellation_slot_is_unspecialised,
|
|
void>::value
|
|
class co_composed_handler_cancellation<Executors, Handler, Return>
|
|
{
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_handler_base :
|
|
public co_composed_handler_cancellation<Executors, Handler, Return>
|
|
{
|
|
public:
|
|
co_composed_handler_base(
|
|
co_composed_promise<Executors, Handler, Return>& p) noexcept
|
|
: p_(&p)
|
|
{
|
|
}
|
|
|
|
co_composed_handler_base(co_composed_handler_base&& other) noexcept
|
|
: p_(std::exchange(other.p_, nullptr))
|
|
{
|
|
}
|
|
|
|
~co_composed_handler_base()
|
|
{
|
|
if (p_) [[unlikely]]
|
|
p_->destroy();
|
|
}
|
|
|
|
co_composed_promise<Executors, Handler, Return>& promise() const noexcept
|
|
{
|
|
return *p_;
|
|
}
|
|
|
|
protected:
|
|
void resume(void* result)
|
|
{
|
|
co_composed_on_suspend on_suspend{};
|
|
std::exchange(p_, nullptr)->resume(p_, result, on_suspend);
|
|
if (on_suspend.fn_)
|
|
on_suspend.fn_(on_suspend.arg_);
|
|
}
|
|
|
|
private:
|
|
co_composed_promise<Executors, Handler, Return>* p_;
|
|
};
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename Signature>
|
|
class co_composed_handler;
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename R, typename... Args>
|
|
class co_composed_handler<Executors, Handler, Return, R(Args...)>
|
|
: public co_composed_handler_base<Executors, Handler, Return>
|
|
{
|
|
public:
|
|
using co_composed_handler_base<Executors,
|
|
Handler, Return>::co_composed_handler_base;
|
|
|
|
using result_type = std::tuple<typename decay<Args>::type...>;
|
|
|
|
template <typename... T>
|
|
void operator()(T&&... args)
|
|
{
|
|
result_type result(std::forward<T>(args)...);
|
|
this->resume(&result);
|
|
}
|
|
|
|
static auto on_resume(void* result)
|
|
{
|
|
auto& args = *static_cast<result_type*>(result);
|
|
if constexpr (sizeof...(Args) == 0)
|
|
return;
|
|
else if constexpr (sizeof...(Args) == 1)
|
|
return std::move(std::get<0>(args));
|
|
else
|
|
return std::move(args);
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename R, typename... Args>
|
|
class co_composed_handler<Executors, Handler,
|
|
Return, R(asio::error_code, Args...)>
|
|
: public co_composed_handler_base<Executors, Handler, Return>
|
|
{
|
|
public:
|
|
using co_composed_handler_base<Executors,
|
|
Handler, Return>::co_composed_handler_base;
|
|
|
|
using args_type = std::tuple<typename decay<Args>::type...>;
|
|
using result_type = std::tuple<asio::error_code, args_type>;
|
|
|
|
template <typename... T>
|
|
void operator()(const asio::error_code& ec, T&&... args)
|
|
{
|
|
result_type result(ec, args_type(std::forward<T>(args)...));
|
|
this->resume(&result);
|
|
}
|
|
|
|
static auto on_resume(void* result)
|
|
{
|
|
auto& [ec, args] = *static_cast<result_type*>(result);
|
|
throw_error(ec);
|
|
if constexpr (sizeof...(Args) == 0)
|
|
return;
|
|
else if constexpr (sizeof...(Args) == 1)
|
|
return std::move(std::get<0>(args));
|
|
else
|
|
return std::move(args);
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename R, typename... Args>
|
|
class co_composed_handler<Executors, Handler,
|
|
Return, R(std::exception_ptr, Args...)>
|
|
: public co_composed_handler_base<Executors, Handler, Return>
|
|
{
|
|
public:
|
|
using co_composed_handler_base<Executors,
|
|
Handler, Return>::co_composed_handler_base;
|
|
|
|
using args_type = std::tuple<typename decay<Args>::type...>;
|
|
using result_type = std::tuple<std::exception_ptr, args_type>;
|
|
|
|
template <typename... T>
|
|
void operator()(std::exception_ptr ex, T&&... args)
|
|
{
|
|
result_type result(std::move(ex), args_type(std::forward<T>(args)...));
|
|
this->resume(&result);
|
|
}
|
|
|
|
static auto on_resume(void* result)
|
|
{
|
|
auto& [ex, args] = *static_cast<result_type*>(result);
|
|
if (ex)
|
|
std::rethrow_exception(ex);
|
|
if constexpr (sizeof...(Args) == 0)
|
|
return;
|
|
else if constexpr (sizeof...(Args) == 1)
|
|
return std::move(std::get<0>(args));
|
|
else
|
|
return std::move(args);
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_promise_return;
|
|
|
|
template <typename Executors, typename Handler>
|
|
class co_composed_promise_return<Executors, Handler, co_composed_returns<>>
|
|
{
|
|
public:
|
|
auto final_suspend() noexcept
|
|
{
|
|
return suspend_never();
|
|
}
|
|
|
|
void return_void() noexcept
|
|
{
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename Signature>
|
|
class co_composed_promise_return_overload;
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename R, typename... Args>
|
|
class co_composed_promise_return_overload<
|
|
Executors, Handler, Return, R(Args...)>
|
|
{
|
|
public:
|
|
using derived_type = co_composed_promise<Executors, Handler, Return>;
|
|
using return_type = std::tuple<Args...>;
|
|
|
|
void return_value(std::tuple<Args...>&& value)
|
|
{
|
|
derived_type& promise = *static_cast<derived_type*>(this);
|
|
promise.state().return_value_ = std::move(value);
|
|
promise.state().work_.reset();
|
|
promise.state().on_suspend_->arg_ = this;
|
|
promise.state().on_suspend_->fn_ =
|
|
[](void* p)
|
|
{
|
|
auto& promise = *static_cast<derived_type*>(p);
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return> composed_handler(promise);
|
|
|
|
Handler handler(std::move(promise.state().handler_));
|
|
return_type result(
|
|
std::move(std::get<return_type>(promise.state().return_value_)));
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return>(std::move(composed_handler));
|
|
|
|
std::apply(std::move(handler), std::move(result));
|
|
};
|
|
}
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename... Signatures>
|
|
class co_composed_promise_return<Executors,
|
|
Handler, co_composed_returns<Signatures...>>
|
|
: public co_composed_promise_return_overload<Executors,
|
|
Handler, co_composed_returns<Signatures...>, Signatures>...
|
|
{
|
|
public:
|
|
auto final_suspend() noexcept
|
|
{
|
|
return suspend_always();
|
|
}
|
|
|
|
using co_composed_promise_return_overload<Executors, Handler,
|
|
co_composed_returns<Signatures...>, Signatures>::return_value...;
|
|
|
|
private:
|
|
template <typename, typename, typename, typename>
|
|
friend class co_composed_promise_return_overload;
|
|
};
|
|
|
|
template <typename Executors, typename Handler, typename Return>
|
|
class co_composed_promise
|
|
: public co_composed_promise_return<Executors, Handler, Return>
|
|
{
|
|
public:
|
|
template <typename... Args>
|
|
void* operator new(std::size_t size,
|
|
co_composed_state<Executors, Handler, Return>& state, Args&&...)
|
|
{
|
|
block_allocator_type allocator(
|
|
(get_associated_allocator)(state.handler_,
|
|
recycling_allocator<void>()));
|
|
|
|
block* base_ptr = std::allocator_traits<block_allocator_type>::allocate(
|
|
allocator, blocks(sizeof(allocator_type)) + blocks(size));
|
|
|
|
new (static_cast<void*>(base_ptr)) allocator_type(std::move(allocator));
|
|
|
|
return base_ptr + blocks(sizeof(allocator_type));
|
|
}
|
|
|
|
template <typename C, typename... Args>
|
|
void* operator new(std::size_t size, C&&,
|
|
co_composed_state<Executors, Handler, Return>& state, Args&&...)
|
|
{
|
|
return co_composed_promise::operator new(size, state);
|
|
}
|
|
|
|
void operator delete(void* ptr, std::size_t size)
|
|
{
|
|
block* base_ptr = static_cast<block*>(ptr) - blocks(sizeof(allocator_type));
|
|
|
|
allocator_type* allocator_ptr = std::launder(
|
|
static_cast<allocator_type*>(static_cast<void*>(base_ptr)));
|
|
|
|
block_allocator_type block_allocator(std::move(*allocator_ptr));
|
|
allocator_ptr->~allocator_type();
|
|
|
|
std::allocator_traits<block_allocator_type>::deallocate(block_allocator,
|
|
base_ptr, blocks(sizeof(allocator_type)) + blocks(size));
|
|
}
|
|
|
|
template <typename... Args>
|
|
co_composed_promise(
|
|
co_composed_state<Executors, Handler, Return>& state, Args&&...)
|
|
: state_(state)
|
|
{
|
|
}
|
|
|
|
template <typename C, typename... Args>
|
|
co_composed_promise(C&&,
|
|
co_composed_state<Executors, Handler, Return>& state, Args&&...)
|
|
: state_(state)
|
|
{
|
|
}
|
|
|
|
void destroy() noexcept
|
|
{
|
|
coroutine_handle<co_composed_promise>::from_promise(*this).destroy();
|
|
}
|
|
|
|
void resume(co_composed_promise*& owner, void* result,
|
|
co_composed_on_suspend& on_suspend)
|
|
{
|
|
state_.on_suspend_ = &on_suspend;
|
|
state_.clear_cancellation_slot();
|
|
owner_ = &owner;
|
|
result_ = result;
|
|
coroutine_handle<co_composed_promise>::from_promise(*this).resume();
|
|
}
|
|
|
|
co_composed_state<Executors, Handler, Return>& state() noexcept
|
|
{
|
|
return state_;
|
|
}
|
|
|
|
void get_return_object() noexcept
|
|
{
|
|
}
|
|
|
|
auto initial_suspend() noexcept
|
|
{
|
|
return suspend_never();
|
|
}
|
|
|
|
void unhandled_exception()
|
|
{
|
|
if (owner_)
|
|
*owner_ = this;
|
|
throw;
|
|
}
|
|
|
|
template <async_operation Op>
|
|
auto await_transform(Op&& op
|
|
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
# if defined(ASIO_HAS_SOURCE_LOCATION)
|
|
, asio::detail::source_location location
|
|
= asio::detail::source_location::current()
|
|
# endif // defined(ASIO_HAS_SOURCE_LOCATION)
|
|
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
)
|
|
{
|
|
class [[nodiscard]] awaitable
|
|
{
|
|
public:
|
|
awaitable(Op&& op, co_composed_promise& promise
|
|
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
# if defined(ASIO_HAS_SOURCE_LOCATION)
|
|
, const asio::detail::source_location& location
|
|
# endif // defined(ASIO_HAS_SOURCE_LOCATION)
|
|
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
)
|
|
: op_(std::forward<Op>(op)),
|
|
promise_(promise)
|
|
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
# if defined(ASIO_HAS_SOURCE_LOCATION)
|
|
, location_(location)
|
|
# endif // defined(ASIO_HAS_SOURCE_LOCATION)
|
|
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
{
|
|
}
|
|
|
|
constexpr bool await_ready() const noexcept
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void await_suspend(coroutine_handle<co_composed_promise>)
|
|
{
|
|
if (promise_.state_.check_for_cancellation_on_suspend(promise_))
|
|
{
|
|
promise_.state_.on_suspend_->arg_ = this;
|
|
promise_.state_.on_suspend_->fn_ =
|
|
[](void* p)
|
|
{
|
|
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
# if defined(ASIO_HAS_SOURCE_LOCATION)
|
|
ASIO_HANDLER_LOCATION((
|
|
static_cast<awaitable*>(p)->location_.file_name(),
|
|
static_cast<awaitable*>(p)->location_.line(),
|
|
static_cast<awaitable*>(p)->location_.function_name()));
|
|
# endif // defined(ASIO_HAS_SOURCE_LOCATION)
|
|
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
std::forward<Op>(static_cast<awaitable*>(p)->op_)(
|
|
co_composed_handler<Executors, Handler,
|
|
Return, completion_signature_of_t<Op>>(
|
|
static_cast<awaitable*>(p)->promise_));
|
|
};
|
|
}
|
|
}
|
|
|
|
auto await_resume()
|
|
{
|
|
return co_composed_handler<Executors, Handler, Return,
|
|
completion_signature_of_t<Op>>::on_resume(promise_.result_);
|
|
}
|
|
|
|
private:
|
|
Op&& op_;
|
|
co_composed_promise& promise_;
|
|
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
# if defined(ASIO_HAS_SOURCE_LOCATION)
|
|
asio::detail::source_location location_;
|
|
# endif // defined(ASIO_HAS_SOURCE_LOCATION)
|
|
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
};
|
|
|
|
state_.check_for_cancellation_on_transform();
|
|
return awaitable{std::forward<Op>(op), *this
|
|
#if defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
# if defined(ASIO_HAS_SOURCE_LOCATION)
|
|
, location
|
|
# endif // defined(ASIO_HAS_SOURCE_LOCATION)
|
|
#endif // defined(ASIO_ENABLE_HANDLER_TRACKING)
|
|
};
|
|
}
|
|
|
|
template <typename... Args>
|
|
auto yield_value(co_composed_completion<Args...>&& result)
|
|
{
|
|
class [[nodiscard]] awaitable
|
|
{
|
|
public:
|
|
awaitable(co_composed_completion<Args...>&& result,
|
|
co_composed_promise& promise)
|
|
: result_(std::move(result)),
|
|
promise_(promise)
|
|
{
|
|
}
|
|
|
|
constexpr bool await_ready() const noexcept
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void await_suspend(coroutine_handle<co_composed_promise>)
|
|
{
|
|
promise_.state_.work_.reset();
|
|
promise_.state_.on_suspend_->arg_ = this;
|
|
promise_.state_.on_suspend_->fn_ =
|
|
[](void* p)
|
|
{
|
|
awaitable& a = *static_cast<awaitable*>(p);
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return> composed_handler(a.promise_);
|
|
|
|
Handler handler(std::move(a.promise_.state_.handler_));
|
|
std::tuple<typename decay<Args>::type...> result(
|
|
std::move(static_cast<std::tuple<Args&&...>>(a.result_)));
|
|
|
|
co_composed_handler_base<Executors, Handler,
|
|
Return>(std::move(composed_handler));
|
|
|
|
std::apply(std::move(handler), std::move(result));
|
|
};
|
|
}
|
|
|
|
void await_resume() noexcept
|
|
{
|
|
}
|
|
|
|
private:
|
|
co_composed_completion<Args...> result_;
|
|
co_composed_promise& promise_;
|
|
};
|
|
|
|
return awaitable{std::move(result), *this};
|
|
}
|
|
|
|
private:
|
|
using allocator_type =
|
|
associated_allocator_t<Handler, recycling_allocator<void>>;
|
|
|
|
union block
|
|
{
|
|
std::max_align_t max_align;
|
|
alignas(allocator_type) char pad[alignof(allocator_type)];
|
|
};
|
|
|
|
using block_allocator_type =
|
|
typename std::allocator_traits<allocator_type>
|
|
::template rebind_alloc<block>;
|
|
|
|
static constexpr std::size_t blocks(std::size_t size)
|
|
{
|
|
return (size + sizeof(block) - 1) / sizeof(block);
|
|
}
|
|
|
|
co_composed_state<Executors, Handler, Return>& state_;
|
|
co_composed_promise** owner_ = nullptr;
|
|
void* result_ = nullptr;
|
|
};
|
|
|
|
template <typename Implementation, typename Executors, typename... Signatures>
|
|
class initiate_co_composed
|
|
{
|
|
public:
|
|
using executor_type = typename composed_io_executors<Executors>::head_type;
|
|
|
|
template <typename I>
|
|
initiate_co_composed(I&& impl, composed_io_executors<Executors>&& executors)
|
|
: implementation_(std::forward<I>(impl)),
|
|
executors_(std::move(executors))
|
|
{
|
|
}
|
|
|
|
executor_type get_executor() const noexcept
|
|
{
|
|
return executors_.head_;
|
|
}
|
|
|
|
template <typename Handler, typename... InitArgs>
|
|
void operator()(Handler&& handler, InitArgs&&... init_args) const &
|
|
{
|
|
using handler_type = typename decay<Handler>::type;
|
|
using returns_type = co_composed_returns<Signatures...>;
|
|
co_composed_on_suspend on_suspend{};
|
|
implementation_(
|
|
co_composed_state<Executors, handler_type, returns_type>(
|
|
executors_, std::forward<Handler>(handler), on_suspend),
|
|
std::forward<InitArgs>(init_args)...);
|
|
if (on_suspend.fn_)
|
|
on_suspend.fn_(on_suspend.arg_);
|
|
}
|
|
|
|
template <typename Handler, typename... InitArgs>
|
|
void operator()(Handler&& handler, InitArgs&&... init_args) &&
|
|
{
|
|
using handler_type = typename decay<Handler>::type;
|
|
using returns_type = co_composed_returns<Signatures...>;
|
|
co_composed_on_suspend on_suspend{};
|
|
std::move(implementation_)(
|
|
co_composed_state<Executors, handler_type, returns_type>(
|
|
std::move(executors_), std::forward<Handler>(handler), on_suspend),
|
|
std::forward<InitArgs>(init_args)...);
|
|
if (on_suspend.fn_)
|
|
on_suspend.fn_(on_suspend.arg_);
|
|
}
|
|
|
|
private:
|
|
Implementation implementation_;
|
|
composed_io_executors<Executors> executors_;
|
|
};
|
|
|
|
template <typename... Signatures, typename Implementation, typename Executors>
|
|
inline initiate_co_composed<Implementation, Executors, Signatures...>
|
|
make_initiate_co_composed(Implementation&& implementation,
|
|
composed_io_executors<Executors>&& executors)
|
|
{
|
|
return initiate_co_composed<
|
|
typename decay<Implementation>::type, Executors, Signatures...>(
|
|
std::forward<Implementation>(implementation), std::move(executors));
|
|
}
|
|
|
|
} // namespace detail
|
|
|
|
template <completion_signature... Signatures,
|
|
typename Implementation, typename... IoObjectsOrExecutors>
|
|
inline auto co_composed(Implementation&& implementation,
|
|
IoObjectsOrExecutors&&... io_objects_or_executors)
|
|
{
|
|
return detail::make_initiate_co_composed<Signatures...>(
|
|
std::forward<Implementation>(implementation),
|
|
detail::make_composed_io_executors(
|
|
detail::get_composed_io_executor(
|
|
std::forward<IoObjectsOrExecutors>(
|
|
io_objects_or_executors))...));
|
|
}
|
|
|
|
} // namespace experimental
|
|
|
|
#if !defined(GENERATING_DOCUMENTATION)
|
|
|
|
template <template <typename, typename> class Associator,
|
|
typename Executors, typename Handler, typename Return,
|
|
typename Signature, typename DefaultCandidate>
|
|
struct associator<Associator,
|
|
experimental::detail::co_composed_handler<
|
|
Executors, Handler, Return, Signature>,
|
|
DefaultCandidate>
|
|
: Associator<Handler, DefaultCandidate>
|
|
{
|
|
static typename Associator<Handler, DefaultCandidate>::type
|
|
get(const experimental::detail::co_composed_handler<
|
|
Executors, Handler, Return, Signature>& h) ASIO_NOEXCEPT
|
|
{
|
|
return Associator<Handler, DefaultCandidate>::get(
|
|
h.promise().state().handler());
|
|
}
|
|
|
|
static ASIO_AUTO_RETURN_TYPE_PREFIX2(
|
|
typename Associator<Handler, DefaultCandidate>::type)
|
|
get(const experimental::detail::co_composed_handler<
|
|
Executors, Handler, Return, Signature>& h,
|
|
const DefaultCandidate& c) ASIO_NOEXCEPT
|
|
ASIO_AUTO_RETURN_TYPE_SUFFIX((
|
|
Associator<Handler, DefaultCandidate>::get(
|
|
h.promise().state().handler(), c)))
|
|
{
|
|
return Associator<Handler, DefaultCandidate>::get(
|
|
h.promise().state().handler(), c);
|
|
}
|
|
};
|
|
|
|
#endif // !defined(GENERATING_DOCUMENTATION)
|
|
|
|
} // namespace asio
|
|
|
|
#if !defined(GENERATING_DOCUMENTATION)
|
|
# if defined(ASIO_HAS_STD_COROUTINE)
|
|
namespace std {
|
|
# else // defined(ASIO_HAS_STD_COROUTINE)
|
|
namespace std { namespace experimental {
|
|
# endif // defined(ASIO_HAS_STD_COROUTINE)
|
|
|
|
template <typename C, typename Executors,
|
|
typename Handler, typename Return, typename... Args>
|
|
struct coroutine_traits<void, C&,
|
|
asio::experimental::detail::co_composed_state<
|
|
Executors, Handler, Return>,
|
|
Args...>
|
|
{
|
|
using promise_type =
|
|
asio::experimental::detail::co_composed_promise<
|
|
Executors, Handler, Return>;
|
|
};
|
|
|
|
template <typename C, typename Executors,
|
|
typename Handler, typename Return, typename... Args>
|
|
struct coroutine_traits<void, C&&,
|
|
asio::experimental::detail::co_composed_state<
|
|
Executors, Handler, Return>,
|
|
Args...>
|
|
{
|
|
using promise_type =
|
|
asio::experimental::detail::co_composed_promise<
|
|
Executors, Handler, Return>;
|
|
};
|
|
|
|
template <typename Executors, typename Handler,
|
|
typename Return, typename... Args>
|
|
struct coroutine_traits<void,
|
|
asio::experimental::detail::co_composed_state<
|
|
Executors, Handler, Return>,
|
|
Args...>
|
|
{
|
|
using promise_type =
|
|
asio::experimental::detail::co_composed_promise<
|
|
Executors, Handler, Return>;
|
|
};
|
|
|
|
# if defined(ASIO_HAS_STD_COROUTINE)
|
|
} // namespace std
|
|
# else // defined(ASIO_HAS_STD_COROUTINE)
|
|
}} // namespace std::experimental
|
|
# endif // defined(ASIO_HAS_STD_COROUTINE)
|
|
#endif // !defined(GENERATING_DOCUMENTATION)
|
|
|
|
#include "asio/detail/pop_options.hpp"
|
|
|
|
#endif // ASIO_IMPL_EXPERIMENTAL_CO_COMPOSED_HPP
|