2
0
mirror of https://github.com/boostorg/asio.git synced 2026-01-19 04:02:09 +00:00

Add redirect_disposition completion token adapter.

This commit is contained in:
Christopher Kohlhoff
2025-11-04 23:42:51 +11:00
parent 10f61afbd8
commit f49a0add53
8 changed files with 1062 additions and 2 deletions

View File

@@ -88,8 +88,10 @@ passed back to the coroutine as a `system_error` exception.
[heading Error Handling]
To perform explicit error handling, rather than the default exception-throwing
behaviour, use the [link boost_asio.reference.as_tuple `as_tuple`] or
[link boost_asio.reference.redirect_error `redirect_error`] completion token adapters.
behaviour, use the [link boost_asio.reference.as_tuple `as_tuple`],
[link boost_asio.reference.redirect_disposition `redirect_disposition`] or
[link boost_asio.reference.redirect_error `redirect_error`] or completion token
adapters.
The `as_tuple` completion token adapter packages the completion
handler arguments into a single tuple, which is then returned as the result of
@@ -296,6 +298,7 @@ protocol in terms of a coroutine:
[link boost_asio.reference.co_spawn co_spawn],
[link boost_asio.reference.detached detached],
[link boost_asio.reference.as_tuple as_tuple],
[link boost_asio.reference.redirect_disposition redirect_disposition],
[link boost_asio.reference.redirect_error redirect_error],
[link boost_asio.reference.awaitable awaitable],
[link boost_asio.reference.use_awaitable_t use_awaitable_t],

View File

@@ -94,6 +94,42 @@ expression will no longer throw an exception on resumption.
// ...
}
[heading redirect_disposition]
The [link boost_asio.reference.redirect_disposition `redirect_disposition`] function
adapts a completion token to capture any [link boost_asio.reference.Disposition
disposition] produced by an operation into a specified variable. In doing so, it
modifies the completion signature to remove the initial disposition parameter.
This example shows the `redirect_disposition` adapter applied to a lambda, to
specify that the disposition should be captured into `my_exception`. The
`error_code` (which is a disposition type) is no longer passed to the completion
handler, but the remaining arguments are passed through as-is.
std::exception_ptr my_exception; // N.B. must be valid until operation completes
// ...
my_socket.async_read_some(my_buffer,
boost::asio::redirect_disposition(
[](std::size_t bytes_transferred)
{
// ...
}, my_exception));
When applied to completion tokens that cause the initiating function to produce
a result, such as [link boost_asio.reference.use_awaitable `use_awaitable`], the
result is returned unmodified. However, if the operation fails, the `co_await`
expression will no longer throw an exception on resumption.
boost::asio::awaitable<void> my_coroutine()
{
// ...
std::exception my_exception;
std::size_t bytes_transferred =
co_await my_socket.async_read_some(my_buffer,
boost::asio::redirect_disposition(boost::asio::use_awaitable, my_exception));
// ...
}
[heading as_tuple]
The [link boost_asio.reference.as_tuple `as_tuple`] adapter can be used to specify

View File

@@ -234,8 +234,10 @@
<member><link linkend="boost_asio.reference.partial_cancellation_slot_binder">partial_cancellation_slot_binder</link></member>
<member><link linkend="boost_asio.reference.partial_executor_binder">partial_executor_binder</link></member>
<member><link linkend="boost_asio.reference.partial_immediate_executor_binder">partial_immediate_executor_binder</link></member>
<member><link linkend="boost_asio.reference.partial_redirect_disposition">partial_redirect_disposition</link></member>
<member><link linkend="boost_asio.reference.prepend_t">prepend_t</link></member>
<member><link linkend="boost_asio.reference.recycling_allocator">recycling_allocator</link></member>
<member><link linkend="boost_asio.reference.redirect_disposition_t">redirect_disposition_t</link></member>
<member><link linkend="boost_asio.reference.redirect_error_t">redirect_error_t</link></member>
<member><link linkend="boost_asio.reference.strand">strand</link></member>
<member><link linkend="boost_asio.reference.thread_pool__basic_executor_type">thread_pool::basic_executor_type</link></member>
@@ -277,6 +279,7 @@
<member><link linkend="boost_asio.reference.make_work_guard">make_work_guard</link></member>
<member><link linkend="boost_asio.reference.post">post</link></member>
<member><link linkend="boost_asio.reference.prepend">prepend</link></member>
<member><link linkend="boost_asio.reference.redirect_disposition">redirect_disposition</link></member>
<member><link linkend="boost_asio.reference.redirect_error">redirect_error</link></member>
<member><link linkend="boost_asio.reference.spawn">spawn</link></member>
<member><link linkend="boost_asio.reference.this_coro__reset_cancellation_state">this_coro::reset_cancellation_state</link></member>

View File

@@ -163,6 +163,7 @@
#include <boost/asio/read_until.hpp>
#include <boost/asio/readable_pipe.hpp>
#include <boost/asio/recycling_allocator.hpp>
#include <boost/asio/redirect_disposition.hpp>
#include <boost/asio/redirect_error.hpp>
#include <boost/asio/registered_buffer.hpp>
#include <boost/asio/require.hpp>

View File

@@ -0,0 +1,455 @@
// impl/redirect_disposition.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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 BOOST_ASIO_IMPL_REDIRECT_DISPOSITION_HPP
#define BOOST_ASIO_IMPL_REDIRECT_DISPOSITION_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/associated_executor.hpp>
#include <boost/asio/associator.hpp>
#include <boost/asio/async_result.hpp>
#include <boost/asio/detail/handler_cont_helpers.hpp>
#include <boost/asio/detail/initiation_base.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
namespace detail {
// Class to adapt a redirect_disposition_t as a completion handler.
template <typename Disposition, typename Handler>
class redirect_disposition_handler
{
public:
typedef void result_type;
template <typename CompletionToken>
redirect_disposition_handler(
redirect_disposition_t<CompletionToken, Disposition> e)
: d_(e.d_),
handler_(static_cast<CompletionToken&&>(e.token_))
{
}
template <typename RedirectedHandler>
redirect_disposition_handler(Disposition& d, RedirectedHandler&& h)
: d_(d),
handler_(static_cast<RedirectedHandler&&>(h))
{
}
void operator()()
{
static_cast<Handler&&>(handler_)();
}
template <typename Arg, typename... Args>
enable_if_t<
!is_same<decay_t<Arg>, Disposition>::value
>
operator()(Arg&& arg, Args&&... args)
{
static_cast<Handler&&>(handler_)(
static_cast<Arg&&>(arg),
static_cast<Args&&>(args)...);
}
template <typename... Args>
void operator()(const Disposition& d, Args&&... args)
{
d_ = d;
static_cast<Handler&&>(handler_)(static_cast<Args&&>(args)...);
}
//private:
Disposition& d_;
Handler handler_;
};
template <typename Handler>
class redirect_disposition_handler<std::exception_ptr, Handler>
{
public:
typedef void result_type;
template <typename CompletionToken>
redirect_disposition_handler(
redirect_disposition_t<CompletionToken, std::exception_ptr> e)
: d_(e.d_),
handler_(static_cast<CompletionToken&&>(e.token_))
{
}
template <typename RedirectedHandler>
redirect_disposition_handler(std::exception_ptr& d, RedirectedHandler&& h)
: d_(d),
handler_(static_cast<RedirectedHandler&&>(h))
{
}
void operator()()
{
static_cast<Handler&&>(handler_)();
}
template <typename Arg, typename... Args>
enable_if_t<
!is_disposition<decay_t<Arg>>::value
>
operator()(Arg&& arg, Args&&... args)
{
static_cast<Handler&&>(handler_)(
static_cast<Arg&&>(arg),
static_cast<Args&&>(args)...);
}
template <typename Disposition, typename... Args>
enable_if_t<
is_disposition<Disposition>::value
>
operator()(const Disposition& d, Args&&... args)
{
d_ = disposition_traits<Disposition>::to_exception_ptr(d);
static_cast<Handler&&>(handler_)(static_cast<Args&&>(args)...);
}
//private:
std::exception_ptr& d_;
Handler handler_;
};
template <typename Disposition, typename Handler>
inline bool asio_handler_is_continuation(
redirect_disposition_handler<Disposition, Handler>* this_handler)
{
return boost_asio_handler_cont_helpers::is_continuation(
this_handler->handler_);
}
template <typename Disposition, typename Signature, typename = void>
struct redirect_disposition_signature
{
typedef Signature type;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(Disposition, Args...)>
{
typedef R type(Args...);
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(const Disposition&, Args...)>
{
typedef R type(Args...);
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(Disposition, Args...) &>
{
typedef R type(Args...) &;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(const Disposition&, Args...) &>
{
typedef R type(Args...) &;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(Disposition, Args...) &&>
{
typedef R type(Args...) &&;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(const Disposition&, Args...) &&>
{
typedef R type(Args...) &&;
};
#if defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(Disposition, Args...) noexcept>
{
typedef R type(Args...) & noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(const Disposition&, Args...) noexcept>
{
typedef R type(Args...) & noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(Disposition, Args...) & noexcept>
{
typedef R type(Args...) & noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(const Disposition&, Args...) & noexcept>
{
typedef R type(Args...) & noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(Disposition, Args...) && noexcept>
{
typedef R type(Args...) && noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
Disposition, R(const Disposition&, Args...) && noexcept>
{
typedef R type(Args...) && noexcept;
};
#endif // defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(Disposition, Args...),
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...);
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(const Disposition&, Args...),
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...);
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(Disposition, Args...) &,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) &;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(const Disposition&, Args...) &,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) &;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(Disposition, Args...) &&,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) &&;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(const Disposition&, Args...) &&,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) &&;
};
#if defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(Disposition, Args...) noexcept,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) & noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(const Disposition&, Args...) noexcept,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) & noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(Disposition, Args...) & noexcept,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) & noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(const Disposition&, Args...) & noexcept,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) & noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(Disposition, Args...) && noexcept,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) && noexcept;
};
template <typename Disposition, typename R, typename... Args>
struct redirect_disposition_signature<
std::exception_ptr, R(const Disposition&, Args...) && noexcept,
enable_if_t<is_disposition<Disposition>::value>>
{
typedef R type(Args...) && noexcept;
};
#endif // defined(BOOST_ASIO_HAS_NOEXCEPT_FUNCTION_TYPE)
} // namespace detail
#if !defined(GENERATING_DOCUMENTATION)
template <typename CompletionToken, typename Disposition, typename Signature>
struct async_result<
redirect_disposition_t<CompletionToken, Disposition>, Signature>
: async_result<CompletionToken,
typename detail::redirect_disposition_signature<
Disposition, Signature>::type>
{
template <typename Initiation>
struct init_wrapper : detail::initiation_base<Initiation>
{
using detail::initiation_base<Initiation>::initiation_base;
template <typename Handler, typename... Args>
void operator()(Handler&& handler,
Disposition* d, Args&&... args) &&
{
static_cast<Initiation&&>(*this)(
detail::redirect_disposition_handler<Disposition, decay_t<Handler>>(
*d, static_cast<Handler&&>(handler)),
static_cast<Args&&>(args)...);
}
template <typename Handler, typename... Args>
void operator()(Handler&& handler,
Disposition* d, Args&&... args) const &
{
static_cast<const Initiation&>(*this)(
detail::redirect_disposition_handler<Disposition, decay_t<Handler>>(
*d, static_cast<Handler&&>(handler)),
static_cast<Args&&>(args)...);
}
};
template <typename Initiation, typename RawCompletionToken, typename... Args>
static auto initiate(Initiation&& initiation,
RawCompletionToken&& token, Args&&... args)
-> decltype(
async_initiate<
conditional_t<
is_const<remove_reference_t<RawCompletionToken>>::value,
const CompletionToken, CompletionToken>,
typename detail::redirect_disposition_signature<
Disposition, Signature>::type>(
declval<init_wrapper<decay_t<Initiation>>>(),
token.token_, &token.d_, static_cast<Args&&>(args)...))
{
return async_initiate<
conditional_t<
is_const<remove_reference_t<RawCompletionToken>>::value,
const CompletionToken, CompletionToken>,
typename detail::redirect_disposition_signature<
Disposition, Signature>::type>(
init_wrapper<decay_t<Initiation>>(
static_cast<Initiation&&>(initiation)),
token.token_, &token.d_, static_cast<Args&&>(args)...);
}
};
template <template <typename, typename> class Associator,
typename Disposition, typename Handler, typename DefaultCandidate>
struct associator<Associator,
detail::redirect_disposition_handler<
Disposition, Handler>, DefaultCandidate>
: Associator<Handler, DefaultCandidate>
{
static typename Associator<Handler, DefaultCandidate>::type get(
const detail::redirect_disposition_handler<Disposition, Handler>& h)
noexcept
{
return Associator<Handler, DefaultCandidate>::get(h.handler_);
}
static auto get(
const detail::redirect_disposition_handler<Disposition, Handler>& h,
const DefaultCandidate& c) noexcept
-> decltype(Associator<Handler, DefaultCandidate>::get(h.handler_, c))
{
return Associator<Handler, DefaultCandidate>::get(h.handler_, c);
}
};
template <typename Disposition, typename... Signatures>
struct async_result<partial_redirect_disposition<Disposition>, Signatures...>
{
template <typename Initiation, typename RawCompletionToken, typename... Args>
static auto initiate(Initiation&& initiation,
RawCompletionToken&& token, Args&&... args)
-> decltype(
async_initiate<Signatures...>(
static_cast<Initiation&&>(initiation),
redirect_disposition_t<
default_completion_token_t<associated_executor_t<Initiation>>,
Disposition>(
default_completion_token_t<associated_executor_t<Initiation>>{},
token.d_),
static_cast<Args&&>(args)...))
{
return async_initiate<Signatures...>(
static_cast<Initiation&&>(initiation),
redirect_disposition_t<
default_completion_token_t<associated_executor_t<Initiation>>,
Disposition>(
default_completion_token_t<associated_executor_t<Initiation>>{},
token.d_),
static_cast<Args&&>(args)...);
}
};
#endif // !defined(GENERATING_DOCUMENTATION)
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#endif // BOOST_ASIO_IMPL_REDIRECT_DISPOSITION_HPP

View File

@@ -0,0 +1,117 @@
//
// redirect_disposition.hpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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 BOOST_ASIO_REDIRECT_DISPOSITION_HPP
#define BOOST_ASIO_REDIRECT_DISPOSITION_HPP
#if defined(_MSC_VER) && (_MSC_VER >= 1200)
# pragma once
#endif // defined(_MSC_VER) && (_MSC_VER >= 1200)
#include <boost/asio/detail/config.hpp>
#include <boost/asio/detail/type_traits.hpp>
#include <boost/asio/disposition.hpp>
#include <boost/asio/detail/push_options.hpp>
namespace boost {
namespace asio {
/// A @ref completion_token adapter used to specify that the disposition
/// produced by an asynchronous operation is captured to a variable.
/**
* The redirect_disposition_t class is used to indicate that any disposition
* produced by an asynchronous operation is captured to a specified variable.
*/
template <typename CompletionToken, BOOST_ASIO_DISPOSITION Disposition>
class redirect_disposition_t
{
public:
/// Constructor.
template <typename T>
redirect_disposition_t(T&& completion_token, Disposition& d)
: token_(static_cast<T&&>(completion_token)),
d_(d)
{
}
//private:
CompletionToken token_;
Disposition& d_;
};
/// A function object type that adapts a @ref completion_token to capture
/// any disposition produced by an asynchronous operation to a variable.
/**
* May also be used directly as a completion token, in which case it adapts the
* asynchronous operation's default completion token (or boost::asio::deferred
* if no default is available).
*/
template <BOOST_ASIO_DISPOSITION Disposition>
class partial_redirect_disposition
{
public:
/// Constructor that specifies the variable used to capture disposition
/// values.
explicit partial_redirect_disposition(Disposition& d)
: d_(d)
{
}
/// Adapt a @ref completion_token to specify that the completion handler
/// should capture disposition values to a variable.
template <typename CompletionToken>
BOOST_ASIO_NODISCARD inline
constexpr redirect_disposition_t<decay_t<CompletionToken>, Disposition>
operator()(CompletionToken&& completion_token) const
{
return redirect_disposition_t<decay_t<CompletionToken>, Disposition>(
static_cast<CompletionToken&&>(completion_token), d_);
}
//private:
Disposition& d_;
};
/// Create a partial completion token adapter that captures disposition values
/// to a variable.
/**
* @note When redirecting to a variable of type @c std::exception_ptr, other
* disposition types will be automatically converted to @c std::exception_ptr.
*/
template <BOOST_ASIO_DISPOSITION Disposition>
BOOST_ASIO_NODISCARD inline partial_redirect_disposition<Disposition>
redirect_disposition(Disposition& d)
{
return partial_redirect_disposition<Disposition>(d);
}
/// Adapt a @ref completion_token to capture disposition values to a variable.
/**
* @note When redirecting to a variable of type @c std::exception_ptr, other
* disposition types will be automatically converted to @c std::exception_ptr.
*/
template <typename CompletionToken, BOOST_ASIO_DISPOSITION Disposition>
BOOST_ASIO_NODISCARD inline
redirect_disposition_t<decay_t<CompletionToken>, Disposition>
redirect_disposition(CompletionToken&& completion_token, Disposition& d)
{
return redirect_disposition_t<decay_t<CompletionToken>, Disposition>(
static_cast<CompletionToken&&>(completion_token), d);
}
} // namespace asio
} // namespace boost
#include <boost/asio/detail/pop_options.hpp>
#include <boost/asio/impl/redirect_disposition.hpp>
#endif // BOOST_ASIO_REDIRECT_DISPOSITION_HPP

View File

@@ -252,6 +252,8 @@ test-suite "asio" :
[ link readable_pipe.cpp : $(USE_SELECT) : readable_pipe_select ]
[ run recycling_allocator.cpp ]
[ run recycling_allocator.cpp : : : $(USE_SELECT) : recycling_allocator_select ]
[ link redirect_disposition.cpp ]
[ link redirect_disposition.cpp : $(USE_SELECT) : redirect_disposition_select ]
[ link redirect_error.cpp ]
[ link redirect_error.cpp : $(USE_SELECT) : redirect_error_select ]
[ link registered_buffer.cpp ]

View File

@@ -0,0 +1,443 @@
//
// redirect_disposition.cpp
// ~~~~~~~~~~~~~~~~~~~~~~~~
//
// Copyright (c) 2003-2025 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)
//
// Disable autolinking for unit tests.
#if !defined(BOOST_ALL_NO_LIB)
#define BOOST_ALL_NO_LIB 1
#endif // !defined(BOOST_ALL_NO_LIB)
// Test that header file is self-contained.
#include <boost/asio/redirect_disposition.hpp>
#include <boost/asio/bind_executor.hpp>
#include <boost/asio/deferred.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/system_timer.hpp>
#include <boost/asio/use_future.hpp>
#include "unit_test.hpp"
struct redirect_disposition_handler
{
int* count_;
explicit redirect_disposition_handler(int* c)
: count_(c)
{
}
void operator()()
{
++(*count_);
}
};
void redirect_disposition_test()
{
boost::asio::io_context io1;
boost::asio::io_context io2;
boost::asio::system_timer timer1(io1);
boost::system::error_code ec = boost::asio::error::would_block;
int count = 0;
timer1.expires_after(boost::asio::chrono::seconds(0));
timer1.async_wait(
boost::asio::redirect_disposition(
boost::asio::bind_executor(io2.get_executor(),
redirect_disposition_handler(&count)), ec));
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 0);
io1.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 0);
io2.run();
BOOST_ASIO_CHECK(!ec);
BOOST_ASIO_CHECK(count == 1);
ec = boost::asio::error::would_block;
timer1.async_wait(
boost::asio::redirect_disposition(
boost::asio::bind_executor(io2.get_executor(),
boost::asio::deferred), ec))(redirect_disposition_handler(&count));
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 1);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 1);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(!ec);
BOOST_ASIO_CHECK(count == 2);
#if defined(BOOST_ASIO_HAS_STD_FUTURE_CLASS)
ec = boost::asio::error::would_block;
std::future<void> f = timer1.async_wait(
boost::asio::redirect_disposition(
boost::asio::bind_executor(io2.get_executor(),
boost::asio::use_future), ec));
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::timeout);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::timeout);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(!ec);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::ready);
#endif // defined(BOOST_ASIO_HAS_STD_FUTURE_CLASS)
}
void partial_redirect_disposition_test()
{
boost::asio::io_context io1;
boost::asio::io_context io2;
boost::asio::system_timer timer1(io1);
boost::system::error_code ec = boost::asio::error::would_block;
int count = 0;
timer1.expires_after(boost::asio::chrono::seconds(0));
timer1.async_wait(boost::asio::redirect_disposition(ec))(
boost::asio::bind_executor(io2.get_executor(),
redirect_disposition_handler(&count)));
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 0);
io1.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 0);
io2.run();
BOOST_ASIO_CHECK(!ec);
BOOST_ASIO_CHECK(count == 1);
ec = boost::asio::error::would_block;
timer1.async_wait(boost::asio::redirect_disposition(ec))(
boost::asio::bind_executor(io2.get_executor(),
boost::asio::deferred))(redirect_disposition_handler(&count));
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 1);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 1);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(!ec);
BOOST_ASIO_CHECK(count == 2);
ec = boost::asio::error::would_block;
timer1.async_wait()(boost::asio::redirect_disposition(ec))(
boost::asio::bind_executor(io2.get_executor(),
boost::asio::deferred))(redirect_disposition_handler(&count));
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 2);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(count == 2);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(!ec);
BOOST_ASIO_CHECK(count == 3);
#if defined(BOOST_ASIO_HAS_STD_FUTURE_CLASS)
ec = boost::asio::error::would_block;
std::future<void> f = timer1.async_wait(boost::asio::redirect_disposition(ec))(
boost::asio::bind_executor(io2.get_executor(), boost::asio::use_future));
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::timeout);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ec == boost::asio::error::would_block);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::timeout);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(!ec);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::ready);
#endif // defined(BOOST_ASIO_HAS_STD_FUTURE_CLASS)
}
void redirect_disposition_to_exception_ptr_test()
{
boost::asio::io_context io1;
boost::asio::io_context io2;
boost::asio::system_timer timer1(io1);
std::exception_ptr ex = nullptr;
int count = 0;
timer1.expires_after(boost::asio::chrono::seconds(100));
timer1.async_wait(
boost::asio::redirect_disposition(
boost::asio::bind_executor(io2.get_executor(),
redirect_disposition_handler(&count)), ex));
timer1.cancel();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 0);
io1.run();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 0);
io2.run();
BOOST_ASIO_CHECK(ex != nullptr);
BOOST_ASIO_CHECK(count == 1);
#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
try
{
std::rethrow_exception(ex);
}
catch (const boost::system::system_error& e)
{
BOOST_ASIO_CHECK(e.code() == boost::asio::error::operation_aborted);
}
#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
ex = nullptr;
timer1.async_wait(
boost::asio::redirect_disposition(
boost::asio::bind_executor(io2.get_executor(),
boost::asio::deferred), ex))(redirect_disposition_handler(&count));
timer1.cancel();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 1);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 1);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(ex != nullptr);
BOOST_ASIO_CHECK(count == 2);
#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
try
{
std::rethrow_exception(ex);
}
catch (const boost::system::system_error& e)
{
BOOST_ASIO_CHECK(e.code() == boost::asio::error::operation_aborted);
}
#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
#if defined(BOOST_ASIO_HAS_STD_FUTURE_CLASS)
ex = nullptr;
std::future<void> f = timer1.async_wait(
boost::asio::redirect_disposition(
boost::asio::bind_executor(io2.get_executor(),
boost::asio::use_future), ex));
timer1.cancel();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::timeout);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::timeout);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(ex != nullptr);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::ready);
#endif // defined(BOOST_ASIO_HAS_STD_FUTURE_CLASS)
}
void partial_redirect_disposition_to_exception_ptr_test()
{
boost::asio::io_context io1;
boost::asio::io_context io2;
boost::asio::system_timer timer1(io1);
std::exception_ptr ex = nullptr;
int count = 0;
timer1.expires_after(boost::asio::chrono::seconds(100));
timer1.async_wait(boost::asio::redirect_disposition(ex))(
boost::asio::bind_executor(io2.get_executor(),
redirect_disposition_handler(&count)));
timer1.cancel();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 0);
io1.run();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 0);
io2.run();
BOOST_ASIO_CHECK(ex != nullptr);
BOOST_ASIO_CHECK(count == 1);
#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
try
{
std::rethrow_exception(ex);
}
catch (const boost::system::system_error& e)
{
BOOST_ASIO_CHECK(e.code() == boost::asio::error::operation_aborted);
}
#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
ex = nullptr;
timer1.async_wait(boost::asio::redirect_disposition(ex))(
boost::asio::bind_executor(io2.get_executor(),
boost::asio::deferred))(redirect_disposition_handler(&count));
timer1.cancel();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 1);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 1);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(ex != nullptr);
BOOST_ASIO_CHECK(count == 2);
#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
try
{
std::rethrow_exception(ex);
}
catch (const boost::system::system_error& e)
{
BOOST_ASIO_CHECK(e.code() == boost::asio::error::operation_aborted);
}
#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
ex = nullptr;
timer1.async_wait()(boost::asio::redirect_disposition(ex))(
boost::asio::bind_executor(io2.get_executor(),
boost::asio::deferred))(redirect_disposition_handler(&count));
timer1.cancel();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 2);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(count == 2);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(ex != nullptr);
BOOST_ASIO_CHECK(count == 3);
#if !defined(BOOST_ASIO_NO_EXCEPTIONS)
try
{
std::rethrow_exception(ex);
}
catch (const boost::system::system_error& e)
{
BOOST_ASIO_CHECK(e.code() == boost::asio::error::operation_aborted);
}
#endif // !defined(BOOST_ASIO_NO_EXCEPTIONS)
#if defined(BOOST_ASIO_HAS_STD_FUTURE_CLASS)
ex = nullptr;
std::future<void> f = timer1.async_wait(boost::asio::redirect_disposition(ex))(
boost::asio::bind_executor(io2.get_executor(), boost::asio::use_future));
timer1.cancel();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::timeout);
io1.restart();
io1.run();
BOOST_ASIO_CHECK(ex == nullptr);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::timeout);
io2.restart();
io2.run();
BOOST_ASIO_CHECK(ex != nullptr);
BOOST_ASIO_CHECK(f.wait_for(std::chrono::seconds(0))
== std::future_status::ready);
#endif // defined(BOOST_ASIO_HAS_STD_FUTURE_CLASS)
}
BOOST_ASIO_TEST_SUITE
(
"redirect_disposition",
BOOST_ASIO_TEST_CASE(redirect_disposition_test)
BOOST_ASIO_TEST_CASE(partial_redirect_disposition_test)
BOOST_ASIO_TEST_CASE(redirect_disposition_to_exception_ptr_test)
BOOST_ASIO_TEST_CASE(partial_redirect_disposition_to_exception_ptr_test)
)