From ca26bbbc876a4fe06638f816db9711106fdf1cdc Mon Sep 17 00:00:00 2001 From: Denis Blank Date: Fri, 3 Apr 2020 19:37:51 +0200 Subject: [PATCH] Remap asio::error::basic_errors::operation_aborted to a default constructed exception_t * Add a successful wait on the asio async timer * Closes #27 * Closes #28 --- .../example-asio/example-asio-integration.cpp | 77 ++++++++++++------- include/continuable/detail/external/asio.hpp | 23 ++++-- 2 files changed, 64 insertions(+), 36 deletions(-) diff --git a/examples/example-asio/example-asio-integration.cpp b/examples/example-asio/example-asio-integration.cpp index 02619c3..4ac80ab 100644 --- a/examples/example-asio/example-asio-integration.cpp +++ b/examples/example-asio/example-asio-integration.cpp @@ -36,8 +36,12 @@ // Queries the NIST daytime service and prints the current date and time void daytime_service(); +// Checks that a timer async_wait returns successfully +void successful_async_wait(); + // Checks that a cancelled timer async_wait fails with -// `asio::error::operation_aborted` +// `asio::error::operation_aborted` and is converted to a default constructed +// cti::exception_t. void cancelled_async_wait(); // Indicates fatal error due to an unexpected failure in the continuation chain. @@ -48,6 +52,8 @@ void check_aborted_operation(cti::exception_t); int main(int, char**) { daytime_service(); + + successful_async_wait(); cancelled_async_wait(); return 0; @@ -69,7 +75,25 @@ void daytime_service() { return asio::async_read_until(socket, asio::dynamic_buffer(buf), '\n', cti::use_continuable); }) - .then([&buf](std::size_t) { puts(buf.data()); }) + .then([&buf](std::size_t) { + puts("Continuation successfully got a daytime response:"); + puts(buf.c_str()); + }) + .fail(&unexpected_error); + + ioc.run(); +} + +void successful_async_wait() { + asio::io_context ioc(1); + asio::steady_timer t(ioc); + + t.expires_after(std::chrono::seconds(1)); + + t.async_wait(cti::use_continuable) + .then([] { + puts("Continuation succeeded after 1s as expected!"); + }) .fail(&unexpected_error); ioc.run(); @@ -93,37 +117,32 @@ void cancelled_async_wait() { } void unexpected_error(cti::exception_t e) { -#if defined(CONTINUABLE_HAS_EXCEPTIONS) - std::rethrow_exception(e); -#else - puts("Continuation failed with unexpected error"); - puts(e.message().data()); - std::terminate(); -#endif -} - -void check_aborted_operation(cti::exception_t ex) { - auto is_expected_error = [](auto err_val) { - if (err_val == asio::error_code(asio::error::operation_aborted)) { - puts("Continuation failed due to aborted async operation, as expected."); - return true; - } - return false; - }; + if (!bool(e)) { + puts("Continuation failed with unexpected cancellation!"); + std::terminate(); + } #if defined(CONTINUABLE_HAS_EXCEPTIONS) try { - std::rethrow_exception(ex); - } catch (asio::system_error const& err) { - if (is_expected_error(err.code())) { - return; - } + std::rethrow_exception(e); + } catch (std::exception const& ex) { + puts("Continuation failed with unexpected exception"); + puts(ex.what()); + } catch (...) { + // Rethrow the exception to the asynchronous unhandled exception handler + std::rethrow_exception(std::current_exception()); } #else - if (is_expected_error(ex)) { - return; - } + puts("Continuation failed with unexpected error"); + puts(e.message().data()); #endif - - unexpected_error(ex); + std::terminate(); +} + +void check_aborted_operation(cti::exception_t ex) { + if (bool(ex)) { + unexpected_error(ex); + } else { + puts("Continuation failed due to aborted async operation, as expected."); + } } diff --git a/include/continuable/detail/external/asio.hpp b/include/continuable/detail/external/asio.hpp index 0064788..5f4d9d8 100644 --- a/include/continuable/detail/external/asio.hpp +++ b/include/continuable/detail/external/asio.hpp @@ -30,10 +30,14 @@ #ifndef CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED #define CONTINUABLE_DETAIL_ASIO_HPP_INCLUDED +#include +#include +#include #include #if defined(ASIO_STANDALONE) #include +#include #include #include @@ -51,6 +55,7 @@ #define CTI_DETAIL_ASIO_NAMESPACE_END } #else #include +#include #include #include @@ -79,10 +84,6 @@ "integrated manually with `cti::promisify`." #endif -#include - -#include - #if defined(CONTINUABLE_HAS_EXCEPTIONS) #include #endif @@ -93,12 +94,14 @@ namespace asio { #if defined(ASIO_STANDALONE) using error_code_t = ::asio::error_code; +using basic_errors_t = ::asio::error::basic_errors; #if defined(CONTINUABLE_HAS_EXCEPTIONS) using system_error_t = ::asio::system_error; #endif #else using error_code_t = ::boost::system::error_code; +using basic_errors_t = ::boost::asio::error::basic_errors; #if defined(CONTINUABLE_HAS_EXCEPTIONS) using system_error_t = ::boost::system::system_error; @@ -112,12 +115,18 @@ auto promise_resolver_handler(Promise&& promise) noexcept { return [promise = std::forward(promise)]( error_code_t e, auto&&... args) mutable noexcept { if (e) { + if (e != basic_errors_t::operation_aborted) { #if defined(CONTINUABLE_HAS_EXCEPTIONS) - promise.set_exception( - std::make_exception_ptr(system_error_t(std::move(e)))); + promise.set_exception( + std::make_exception_ptr(system_error_t(std::move(e)))); #else - promise.set_exception(cti::exception_t(e.value(), e.category())); + 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{}); + } } else { promise.set_value(std::forward(args)...); }