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
154
include/continuable/detail/external/asio.hpp
vendored
154
include/continuable/detail/external/asio.hpp
vendored
@ -30,62 +30,63 @@
|
|||||||
#ifndef CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
|
#ifndef CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
|
||||||
#define CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
|
#define CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED
|
||||||
|
|
||||||
|
#include <array>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <continuable/continuable-base.hpp>
|
#include <continuable/continuable-base.hpp>
|
||||||
#include <continuable/detail/core/base.hpp>
|
#include <continuable/detail/core/base.hpp>
|
||||||
#include <continuable/detail/features.hpp>
|
#include <continuable/detail/features.hpp>
|
||||||
|
|
||||||
#if defined(ASIO_STANDALONE)
|
#if defined(ASIO_STANDALONE)
|
||||||
#include <asio/async_result.hpp>
|
# include <asio/async_result.hpp>
|
||||||
#include <asio/error.hpp>
|
# include <asio/error.hpp>
|
||||||
#include <asio/error_code.hpp>
|
# include <asio/error_code.hpp>
|
||||||
#include <asio/version.hpp>
|
# include <asio/version.hpp>
|
||||||
|
|
||||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||||
#include <asio/system_error.hpp>
|
# include <asio/system_error.hpp>
|
||||||
#endif
|
# endif
|
||||||
|
|
||||||
#if (ASIO_VERSION < 101300) // 1.13.0
|
# if (ASIO_VERSION < 101300) // 1.13.0
|
||||||
#define CTI_DETAIL_ASIO_HAS_NO_INTEGRATION
|
# define CTI_DETAIL_ASIO_HAS_NO_INTEGRATION
|
||||||
#elif (ASIO_VERSION < 101600) // 1.16.0 (boost 1.72 baseline)
|
# elif (ASIO_VERSION < 101600) // 1.16.0 (boost 1.72 baseline)
|
||||||
#define CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION
|
# define CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION
|
||||||
#endif
|
# endif
|
||||||
|
|
||||||
#define CTI_DETAIL_ASIO_NAMESPACE_BEGIN namespace asio {
|
# define CTI_DETAIL_ASIO_NAMESPACE_BEGIN namespace asio {
|
||||||
#define CTI_DETAIL_ASIO_NAMESPACE_END }
|
# define CTI_DETAIL_ASIO_NAMESPACE_END }
|
||||||
#else
|
#else
|
||||||
#include <boost/asio/async_result.hpp>
|
# include <boost/asio/async_result.hpp>
|
||||||
#include <boost/asio/error.hpp>
|
# include <boost/asio/error.hpp>
|
||||||
#include <boost/system/error_code.hpp>
|
# include <boost/system/error_code.hpp>
|
||||||
#include <boost/version.hpp>
|
# include <boost/version.hpp>
|
||||||
|
|
||||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||||
#include <boost/system/system_error.hpp>
|
# include <boost/system/system_error.hpp>
|
||||||
#endif
|
# endif
|
||||||
|
|
||||||
#if (BOOST_VERSION < 107000) // 1.70
|
# if (BOOST_VERSION < 107000) // 1.70
|
||||||
#define CTI_DETAIL_ASIO_HAS_NO_INTEGRATION
|
# define CTI_DETAIL_ASIO_HAS_NO_INTEGRATION
|
||||||
#elif (BOOST_VERSION < 107200) // 1.72
|
# elif (BOOST_VERSION < 107200) // 1.72
|
||||||
#define CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION
|
# define CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION
|
||||||
#endif
|
# endif
|
||||||
|
|
||||||
#define CTI_DETAIL_ASIO_NAMESPACE_BEGIN \
|
# define CTI_DETAIL_ASIO_NAMESPACE_BEGIN \
|
||||||
namespace boost { \
|
namespace boost { \
|
||||||
namespace asio {
|
namespace asio {
|
||||||
#define CTI_DETAIL_ASIO_NAMESPACE_END \
|
# define CTI_DETAIL_ASIO_NAMESPACE_END \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CTI_DETAIL_ASIO_HAS_NO_INTEGRATION)
|
#if defined(CTI_DETAIL_ASIO_HAS_NO_INTEGRATION)
|
||||||
#error "First-class ASIO support for continuable requires the form of "\
|
# error "First-class ASIO support for continuable requires the form of "\
|
||||||
"`async_result` with an `initiate` static member function, which was added " \
|
"`async_result` with an `initiate` static member function, which was added " \
|
||||||
"in standalone ASIO 1.13.0 and Boost ASIO 1.70. Older versions can be " \
|
"in standalone ASIO 1.13.0 and Boost ASIO 1.70. Older versions can be " \
|
||||||
"integrated manually with `cti::promisify`."
|
"integrated manually with `cti::promisify`."
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||||
#include <exception>
|
# include <exception>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
namespace cti {
|
namespace cti {
|
||||||
@ -96,40 +97,42 @@ namespace asio {
|
|||||||
using error_code_t = ::asio::error_code;
|
using error_code_t = ::asio::error_code;
|
||||||
using basic_errors_t = ::asio::error::basic_errors;
|
using basic_errors_t = ::asio::error::basic_errors;
|
||||||
|
|
||||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||||
using system_error_t = ::asio::system_error;
|
using system_error_t = ::asio::system_error;
|
||||||
#endif
|
# endif
|
||||||
#else
|
#else
|
||||||
using error_code_t = ::boost::system::error_code;
|
using error_code_t = ::boost::system::error_code;
|
||||||
using basic_errors_t = ::boost::asio::error::basic_errors;
|
using basic_errors_t = ::boost::asio::error::basic_errors;
|
||||||
|
|
||||||
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
# if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||||
using system_error_t = ::boost::system::system_error;
|
using system_error_t = ::boost::system::system_error;
|
||||||
#endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Binds `promise` to the first argument of a continuable resolver, giving it
|
// Binds `promise` to the first argument of a continuable resolver, giving it
|
||||||
// the signature of an ASIO handler.
|
// the signature of an ASIO handler.
|
||||||
template <typename Promise>
|
template <typename Promise, typename Token>
|
||||||
auto promise_resolver_handler(Promise&& promise) noexcept {
|
auto promise_resolver_handler(Promise&& promise, Token&& token) noexcept {
|
||||||
return [promise = std::forward<Promise>(promise)](
|
return [promise = std::forward<Promise>(promise),
|
||||||
error_code_t e, auto&&... args) mutable noexcept {
|
token = std::forward<Token>(token)](error_code_t e,
|
||||||
|
auto&&... args) mutable noexcept {
|
||||||
if (e) {
|
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)
|
#if defined(CONTINUABLE_HAS_EXCEPTIONS)
|
||||||
promise.set_exception(
|
promise.set_exception(
|
||||||
std::make_exception_ptr(system_error_t(std::move(e))));
|
std::make_exception_ptr(system_error_t(std::move(e))));
|
||||||
#else
|
#else
|
||||||
promise.set_exception(exception_t(e.value(), e.category()));
|
promise.set_exception(exception_t(e.value(), e.category()));
|
||||||
#endif
|
#endif
|
||||||
} else {
|
return;
|
||||||
// Continuable uses a default constructed exception type to signal
|
}
|
||||||
// cancellation to the followed asynchronous control flow.
|
|
||||||
promise.set_exception(exception_t{});
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
promise.set_value(std::forward<decltype(args)>(args)...);
|
|
||||||
}
|
}
|
||||||
|
promise.set_value(std::forward<decltype(args)>(args)...);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -153,8 +156,53 @@ struct initiate_make_continuable<void(error_code_t, Args...)> {
|
|||||||
|
|
||||||
template <typename... Args>
|
template <typename... Args>
|
||||||
struct initiate_make_continuable<void(error_code_t const&, Args...)>
|
struct initiate_make_continuable<void(error_code_t const&, Args...)>
|
||||||
: initiate_make_continuable<void(error_code_t, 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 asio
|
||||||
} // namespace detail
|
} // namespace detail
|
||||||
} // namespace cti
|
} // 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>
|
#include <continuable/detail/utility/traits.hpp>
|
||||||
|
|
||||||
namespace cti {
|
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
|
/// Type used as an ASIO completion token to specify an asynchronous operation
|
||||||
/// that should return a continuable_base.
|
/// 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
|
/// \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
|
/// \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
|
} // namespace cti
|
||||||
|
|
||||||
CTI_DETAIL_ASIO_NAMESPACE_BEGIN
|
CTI_DETAIL_ASIO_NAMESPACE_BEGIN
|
||||||
|
|
||||||
template <typename Signature>
|
template <typename Signature, typename Matcher>
|
||||||
class async_result<cti::use_continuable_t, Signature> {
|
class async_result<cti::use_continuable_t<Matcher>, Signature> {
|
||||||
public:
|
public:
|
||||||
#if defined(CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION)
|
#if defined(CTI_DETAIL_ASIO_HAS_EXPLICIT_RET_TYPE_INTEGRATION)
|
||||||
using return_type = typename cti::detail::asio::initiate_make_continuable<
|
using return_type = typename cti::detail::asio::initiate_make_continuable<
|
||||||
@ -80,16 +153,16 @@ public:
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <typename Initiation, typename... Args>
|
template <typename Initiation, typename... Args>
|
||||||
static auto initiate(Initiation initiation, cti::use_continuable_t,
|
static auto initiate(Initiation initiation,
|
||||||
Args... args) {
|
cti::use_continuable_t<Matcher> token, Args... args) {
|
||||||
return cti::detail::asio::initiate_make_continuable<Signature>{}(
|
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)...)](
|
init_args = std::make_tuple(std::move(args)...)](
|
||||||
auto&& promise) mutable {
|
auto&& promise) mutable {
|
||||||
cti::detail::traits::unpack(
|
cti::detail::traits::unpack(
|
||||||
[initiation = std::move(initiation),
|
[initiation = std::move(initiation),
|
||||||
handler = cti::detail::asio::promise_resolver_handler(
|
handler = cti::detail::asio::promise_resolver_handler(
|
||||||
std::forward<decltype(promise)>(promise))](
|
std::forward<decltype(promise)>(promise), std::move(token))](
|
||||||
auto&&... args) mutable {
|
auto&&... args) mutable {
|
||||||
std::move(initiation)(std::move(handler),
|
std::move(initiation)(std::move(handler),
|
||||||
std::forward<decltype(args)>(args)...);
|
std::forward<decltype(args)>(args)...);
|
||||||
|
|||||||
@ -44,9 +44,7 @@ public:
|
|||||||
}) {}
|
}) {}
|
||||||
|
|
||||||
~async_test_helper() {
|
~async_test_helper() {
|
||||||
assert(work_);
|
cancel();
|
||||||
timer_.cancel();
|
|
||||||
work_.reset();
|
|
||||||
thread_.join();
|
thread_.join();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +53,11 @@ public:
|
|||||||
return timer_.async_wait(use_continuable);
|
return timer_.async_wait(use_continuable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void cancel() {
|
||||||
|
timer_.cancel();
|
||||||
|
work_.reset();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
asio::io_context context_;
|
asio::io_context context_;
|
||||||
asio::steady_timer timer_;
|
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));
|
result<> res = helper.wait_for(500ms).apply(cti::transforms::wait_for(50ms));
|
||||||
ASSERT_FALSE(res.is_exception());
|
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