mirror of
https://github.com/Naios/continuable.git
synced 2025-12-06 16:56:44 +08:00
Improve the use_continuable_t asio completion tokens such that special mappings are aptable
* Also allow to ignore certain error types through use_continuable_ignoring * Ref #32
This commit is contained in:
parent
d80f5ef3ec
commit
8187c16ede
70
include/continuable/detail/external/asio.hpp
vendored
70
include/continuable/detail/external/asio.hpp
vendored
@ -30,6 +30,7 @@
|
||||
#ifndef CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
|
||||
#define CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
|
||||
|
||||
#include <array>
|
||||
#include <utility>
|
||||
#include <continuable/continuable-base.hpp>
|
||||
#include <continuable/detail/core/base.hpp>
|
||||
@ -110,26 +111,28 @@ using system_error_t = ::boost::system::system_error;
|
||||
|
||||
// Binds `promise` to the first argument of a continuable resolver, giving it
|
||||
// the signature of an ASIO handler.
|
||||
template <typename Promise>
|
||||
auto promise_resolver_handler(Promise&& promise) noexcept {
|
||||
return [promise = std::forward<Promise>(promise)](
|
||||
error_code_t e, auto&&... args) mutable noexcept {
|
||||
template <typename Promise, typename Token>
|
||||
auto promise_resolver_handler(Promise&& promise, Token&& token) noexcept {
|
||||
return [promise = std::forward<Promise>(promise),
|
||||
token = std::forward<Token>(token)](error_code_t e,
|
||||
auto&&... args) mutable noexcept {
|
||||
if (e) {
|
||||
if (e != basic_errors_t::operation_aborted) {
|
||||
if (!token.is_ignored(e)) {
|
||||
if (token.is_cancellation(e)) {
|
||||
promise.set_canceled();
|
||||
return;
|
||||
} else {
|
||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||
promise.set_exception(
|
||||
std::make_exception_ptr(system_error_t(std::move(e))));
|
||||
#else
|
||||
promise.set_exception(exception_t(e.value(), e.category()));
|
||||
#endif
|
||||
} else {
|
||||
// Continuable uses a default constructed exception type to signal
|
||||
// cancellation to the followed asynchronous control flow.
|
||||
promise.set_exception(exception_t{});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
promise.set_value(std::forward<decltype(args)>(args)...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@ -155,6 +158,51 @@ template <typename... Args>
|
||||
struct initiate_make_continuable<void(error_code_t const&, Args...)>
|
||||
: initiate_make_continuable<void(error_code_t, Args...)> {};
|
||||
|
||||
struct map_default {
|
||||
constexpr map_default() noexcept {}
|
||||
|
||||
bool is_cancellation(error_code_t const& ec) const noexcept {
|
||||
// Continuable uses a default constructed exception type to signal
|
||||
// cancellation to the followed asynchronous control flow.
|
||||
return ec == basic_errors_t::operation_aborted;
|
||||
}
|
||||
bool is_ignored(error_code_t const& /*ec*/) const noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
struct map_none {
|
||||
constexpr map_none() noexcept {}
|
||||
|
||||
bool is_cancellation(error_code_t const& /*ec*/) const noexcept {
|
||||
return false;
|
||||
}
|
||||
bool is_ignored(error_code_t const& /*ec*/) const noexcept {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::size_t Size>
|
||||
class map_ignore {
|
||||
public:
|
||||
map_ignore(std::array<basic_errors_t, Size> ignored) noexcept
|
||||
: ignored_(ignored) {}
|
||||
|
||||
bool is_cancellation(error_code_t const& ec) const noexcept {
|
||||
return ec == basic_errors_t::operation_aborted;
|
||||
}
|
||||
bool is_ignored(error_code_t const& ec) const noexcept {
|
||||
for (basic_errors_t ignored : ignored_) {
|
||||
if (ec == ignored) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private:
|
||||
std::array<basic_errors_t, Size> ignored_;
|
||||
};
|
||||
} // namespace asio
|
||||
} // namespace detail
|
||||
} // namespace cti
|
||||
|
||||
91
include/continuable/external/asio.hpp
vendored
91
include/continuable/external/asio.hpp
vendored
@ -35,6 +35,21 @@
|
||||
#include <continuable/detail/utility/traits.hpp>
|
||||
|
||||
namespace cti {
|
||||
/// The error code type used by your asio distribution
|
||||
///
|
||||
/// \since 4.1.0
|
||||
using asio_error_code_t = detail::asio::error_code_t;
|
||||
|
||||
/// The basic error code enum used by your asio distribution
|
||||
///
|
||||
/// \since 4.1.0
|
||||
using asio_basic_errors_t = detail::asio::basic_errors_t;
|
||||
|
||||
/// The system error type used by your asio distribution
|
||||
///
|
||||
/// \since 4.1.0
|
||||
using asio_system_error_t = detail::asio::system_error_t;
|
||||
|
||||
/// Type used as an ASIO completion token to specify an asynchronous operation
|
||||
/// that should return a continuable_base.
|
||||
///
|
||||
@ -60,19 +75,77 @@ namespace cti {
|
||||
/// });
|
||||
/// ```
|
||||
///
|
||||
/// \tparam Mapper The token can be instantiated with a custom mapper
|
||||
/// for asio error codes which makes it possible to ignore
|
||||
/// errors or treat them as cancellation types.
|
||||
/// The mapper has the following form:
|
||||
/// ```
|
||||
/// struct my_mapper {
|
||||
/// constexpr my_mapper() noexcept {}
|
||||
///
|
||||
/// /// Returns true when the error_code_t is a type which represents
|
||||
/// /// cancellation and
|
||||
/// bool is_cancellation(error_code_t const& /*ec*/) const noexcept {
|
||||
/// return false;
|
||||
/// }
|
||||
/// bool is_ignored(error_code_t const& /*ec*/) const noexcept {
|
||||
/// return false;
|
||||
/// }
|
||||
/// };
|
||||
/// ```
|
||||
///
|
||||
/// \attention `asio::error::basic_errors::operation_aborted` errors returned
|
||||
/// by asio are automatically transformed into a default constructed
|
||||
/// exception type which represents "operation canceled" by the
|
||||
/// user or program. If you intend to retrieve the full
|
||||
/// asio::error_code without remapping use the use_continuable_raw_t
|
||||
/// completion token instead!
|
||||
///
|
||||
/// \since 4.0.0
|
||||
struct use_continuable_t {};
|
||||
template <typename Mapper = detail::asio::map_default>
|
||||
struct use_continuable_t : public Mapper {
|
||||
using Mapper::Mapper;
|
||||
};
|
||||
|
||||
/// Special value for instance of `asio_token_t`
|
||||
/// \copydoc use_continuable_t
|
||||
///
|
||||
/// The raw async completion handler token does not remap the asio error
|
||||
/// `asio::error::basic_errors::operation_aborted` to a default constructed
|
||||
/// exception type.
|
||||
///
|
||||
/// \since 4.1.0
|
||||
using use_continuable_raw_t = use_continuable_t<detail::asio::map_none>;
|
||||
|
||||
/// Special value for instance of use_continuable_t which performs remapping
|
||||
/// of asio error codes to align the cancellation behaviour with the library.
|
||||
///
|
||||
/// \copydetails use_continuable_t
|
||||
constexpr use_continuable_t use_continuable{};
|
||||
constexpr use_continuable_t<> use_continuable{};
|
||||
|
||||
/// Special value for instance of use_continuable_raw_t which doesn't perform
|
||||
/// remapping of asio error codes and rethrows the raw error code.
|
||||
///
|
||||
/// \copydetails use_continuable_raw_t
|
||||
constexpr use_continuable_raw_t use_continuable_raw{};
|
||||
|
||||
/// Represents a special asio completion token which treats the given
|
||||
/// asio basic error codes as success instead of failure.
|
||||
///
|
||||
/// `asio::error::basic_errors::operation_aborted` is mapped
|
||||
/// as cancellation token.
|
||||
///
|
||||
/// \since 4.1.0
|
||||
template <typename... Args>
|
||||
auto use_continuable_ignoring(Args&&... args) noexcept {
|
||||
return use_continuable_t<detail::asio::map_ignore<sizeof...(Args)>>{
|
||||
{asio_basic_errors_t(std::forward<Args>(args))...}};
|
||||
}
|
||||
} // namespace cti
|
||||
|
||||
CTI_DETAIL_ASIO_NAMESPACE_BEGIN
|
||||
|
||||
template <typename Signature>
|
||||
class async_result<cti::use_continuable_t, Signature> {
|
||||
template <typename Signature, typename Matcher>
|
||||
class async_result<cti::use_continuable_t<Matcher>, Signature> {
|
||||
public:
|
||||
#if defined(CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION)
|
||||
using return_type = typename cti::detail::asio::initiate_make_continuable<
|
||||
@ -80,16 +153,16 @@ public:
|
||||
#endif
|
||||
|
||||
template <typename Initiation, typename... Args>
|
||||
static auto initiate(Initiation initiation, cti::use_continuable_t,
|
||||
Args... args) {
|
||||
static auto initiate(Initiation initiation,
|
||||
cti::use_continuable_t<Matcher> token, Args... args) {
|
||||
return cti::detail::asio::initiate_make_continuable<Signature>{}(
|
||||
[initiation = std::move(initiation),
|
||||
[initiation = std::move(initiation), token = std::move(token),
|
||||
init_args = std::make_tuple(std::move(args)...)](
|
||||
auto&& promise) mutable {
|
||||
cti::detail::traits::unpack(
|
||||
[initiation = std::move(initiation),
|
||||
handler = cti::detail::asio::promise_resolver_handler(
|
||||
std::forward<decltype(promise)>(promise))](
|
||||
std::forward<decltype(promise)>(promise), std::move(token))](
|
||||
auto&&... args) mutable {
|
||||
std::move(initiation)(std::move(handler),
|
||||
std::forward<decltype(args)>(args)...);
|
||||
|
||||
@ -44,9 +44,7 @@ public:
|
||||
}) {}
|
||||
|
||||
~async_test_helper() {
|
||||
assert(work_);
|
||||
timer_.cancel();
|
||||
work_.reset();
|
||||
cancel();
|
||||
thread_.join();
|
||||
}
|
||||
|
||||
@ -55,6 +53,11 @@ public:
|
||||
return timer_.async_wait(use_continuable);
|
||||
}
|
||||
|
||||
void cancel() {
|
||||
timer_.cancel();
|
||||
work_.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
asio::io_context context_;
|
||||
asio::steady_timer timer_;
|
||||
@ -141,3 +144,53 @@ TYPED_TEST(single_dimension_tests, wait_for_test_async) {
|
||||
result<> res = helper.wait_for(500ms).apply(cti::transforms::wait_for(50ms));
|
||||
ASSERT_FALSE(res.is_exception());
|
||||
}
|
||||
|
||||
TYPED_TEST(single_dimension_tests, token_remap_canceled) {
|
||||
asio::io_context io(1);
|
||||
asio::steady_timer timer(io, 50ms);
|
||||
|
||||
result<> value;
|
||||
timer.async_wait(use_continuable).next([&](auto&&... args) {
|
||||
value = result<>::from(std::forward<decltype(args)>(args)...);
|
||||
});
|
||||
|
||||
timer.cancel();
|
||||
io.run();
|
||||
|
||||
ASSERT_TRUE(value.is_exception());
|
||||
ASSERT_FALSE(bool(value.get_exception()));
|
||||
}
|
||||
|
||||
TYPED_TEST(single_dimension_tests, token_remap_none_raw) {
|
||||
asio::io_context io(1);
|
||||
asio::steady_timer timer(io, 50ms);
|
||||
|
||||
result<> value;
|
||||
timer.async_wait(use_continuable_raw).next([&](auto&&... args) {
|
||||
value = result<>::from(std::forward<decltype(args)>(args)...);
|
||||
});
|
||||
|
||||
timer.cancel();
|
||||
io.run();
|
||||
|
||||
ASSERT_TRUE(value.is_exception());
|
||||
ASSERT_TRUE(bool(value.get_exception()));
|
||||
}
|
||||
|
||||
TYPED_TEST(single_dimension_tests, token_remap_ignore) {
|
||||
asio::io_context io(1);
|
||||
asio::steady_timer timer(io, 50ms);
|
||||
|
||||
result<> value;
|
||||
timer
|
||||
.async_wait(
|
||||
use_continuable_ignoring(asio_basic_errors_t::operation_aborted))
|
||||
.next([&](auto&&... args) {
|
||||
value = result<>::from(std::forward<decltype(args)>(args)...);
|
||||
});
|
||||
|
||||
timer.cancel();
|
||||
io.run();
|
||||
|
||||
ASSERT_TRUE(value.is_value());
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user