mirror of
https://github.com/ceph/ceph
synced 2026-08-02 07:03:18 +00:00
common/async: Add redirect_error.h
Straightforwardly update `redirect_error` to work with Asio's `disposition` concept generally. No point in sending upstream because Asio never accepts contributions. Signed-off-by: Adam C. Emerson <aemerson@redhat.com>
This commit is contained in:
parent
52c80fb413
commit
b0f2bb760c
6
COPYING
6
COPYING
@ -211,6 +211,12 @@ Copyright: 2020 Red Hat <contact@redhat.com>
|
||||
2003-2019 Christopher M. Kohlhoff <chris@kohlhoff.com>
|
||||
License: Boost Software License, Version 1.0
|
||||
|
||||
Files: src/common/async/redirect_error.h,
|
||||
src/test/common/test_async_redirect_error.cc
|
||||
Copyright: 2025 Contributors to the Ceph Project
|
||||
2003-2024 Christopher M. Kohlhoff <chris@kohlhoff.com>
|
||||
License: Boost Software License, Version 1.0
|
||||
|
||||
Files: src/script/backport-create-issue
|
||||
Copyright: 2015 Red Hat <contact@redhat.com>
|
||||
2018 SUSE LLC
|
||||
|
||||
344
src/common/async/redirect_error.h
Normal file
344
src/common/async/redirect_error.h
Normal file
@ -0,0 +1,344 @@
|
||||
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
||||
// vim: ts=8 sw=2 smarttab ft=cpp
|
||||
/*
|
||||
* Ceph - scalable distributed file system
|
||||
*/
|
||||
|
||||
// Copyright: 2025 Contributors to the Ceph Project
|
||||
// Based on boost/asio/redirect_error.hpp and
|
||||
// boost/asio/impl/redirect_error.hpp which are
|
||||
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include <boost/asio/associated_executor.hpp>
|
||||
#include <boost/asio/async_result.hpp>
|
||||
#include <boost/asio/default_completion_token.hpp>
|
||||
#include <boost/asio/disposition.hpp>
|
||||
#include <boost/asio/handler_continuation_hook.hpp>
|
||||
|
||||
/// \file common/async/redirect_error.h
|
||||
///
|
||||
/// \brief `redirect_error` that knows about `dispositions`.
|
||||
///
|
||||
/// Asio has a very useful concept called a `disposition` that
|
||||
/// generalizes the notion of an error code. Unfortunately
|
||||
/// `redirect_error` doesn't know about dispositions, making it less
|
||||
/// useful.
|
||||
|
||||
namespace ceph::async {
|
||||
|
||||
/// A @ref completion_token adapter used to specify that an error
|
||||
/// produced by an asynchronous operation is captured to a specified
|
||||
/// variable. The variable must be of a `disposition` type.
|
||||
/**
|
||||
* The redirect_error_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_error_t {
|
||||
public: // Sigh
|
||||
CompletionToken token;
|
||||
Disposition& disposition;
|
||||
|
||||
template<typename CT>
|
||||
redirect_error_t(CT&& token, Disposition& disposition)
|
||||
: token(std::forward<CT>(token)), disposition(disposition) {}
|
||||
};
|
||||
|
||||
/// A function object type that adapts a @ref completion_token to capture
|
||||
/// disposition values 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_error {
|
||||
public:
|
||||
Disposition& disposition;
|
||||
|
||||
/// Constructor that specifies the variable used to capture disposition values.
|
||||
explicit partial_redirect_error(Disposition& disposition)
|
||||
: disposition(disposition) {}
|
||||
|
||||
/// Adapt a @ref completion_token to specify that the completion handler
|
||||
/// should capture disposition values to a variable.
|
||||
template<typename CompletionToken>
|
||||
[[nodiscard]] constexpr inline auto
|
||||
operator ()(CompletionToken&& token) const {
|
||||
return redirect_error_t<std::decay_t<CompletionToken>, Disposition>{
|
||||
std::forward<CompletionToken>(token), disposition};
|
||||
}
|
||||
};
|
||||
|
||||
/// Create a partial completion token adapter that captures disposition values
|
||||
/// to a variable.
|
||||
template<boost::asio::disposition Disposition>
|
||||
[[nodiscard]] inline auto redirect_error(Disposition& d)
|
||||
{
|
||||
return partial_redirect_error<std::decay_t<Disposition>>{d};
|
||||
}
|
||||
|
||||
/// Adapt a @ref completion_token to capture disposition values to a variable.
|
||||
template<typename CompletionToken, boost::asio::disposition Disposition>
|
||||
[[nodiscard]] inline auto redirect_error(CompletionToken&& token,
|
||||
Disposition& d)
|
||||
{
|
||||
return redirect_error_t<std::decay_t<CompletionToken>,
|
||||
std::decay_t<Disposition>>{
|
||||
std::forward<CompletionToken>(token), d};
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
template<typename Handler, boost::asio::disposition Disposition>
|
||||
class redirect_error_handler {
|
||||
public:
|
||||
Disposition& disposition;
|
||||
// Essentially a call-once function, invoked as an rvalue.
|
||||
Handler handler;
|
||||
|
||||
using result_type = void;
|
||||
template<typename CompletionToken>
|
||||
redirect_error_handler(
|
||||
redirect_error_t<std::decay_t<CompletionToken>,
|
||||
std::decay_t<Disposition>> re)
|
||||
: disposition(re.disposition), handler(std::move(re.token)) {}
|
||||
|
||||
template<typename RedirectedHandler>
|
||||
redirect_error_handler(Disposition &disposition, RedirectedHandler&& handler)
|
||||
: disposition(disposition),
|
||||
handler(std::forward<RedirectedHandler>(handler)) {}
|
||||
|
||||
|
||||
void operator ()() {
|
||||
std::move(handler)();
|
||||
}
|
||||
|
||||
template<typename Arg0, typename ...Args>
|
||||
std::enable_if_t<!std::is_same_v<std::decay_t<Arg0>,
|
||||
Disposition>>
|
||||
operator ()(Arg0&& arg0, Args ...args) {
|
||||
std::move(handler)(std::forward<Arg0>(arg0), std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename... Args>
|
||||
void operator ()(const Disposition& d, Args&& ...args) {
|
||||
disposition = d;
|
||||
std::move(handler)(std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<boost::asio::disposition Disposition, typename Handler>
|
||||
inline bool asio_handler_is_continuation(
|
||||
redirect_error_handler<Disposition, Handler>* this_handler)
|
||||
{
|
||||
using boost::asio::asio_handler_is_continuation;
|
||||
return asio_handler_is_continuation(&this_handler->handler);
|
||||
}
|
||||
|
||||
template<typename Signature>
|
||||
struct redirect_error_signature
|
||||
{
|
||||
using type = Signature;
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(Disposition, Args...)>
|
||||
{
|
||||
typedef R type(Args...);
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(const Disposition&, Args...)>
|
||||
{
|
||||
typedef R type(Args...);
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(Disposition, Args...) &>
|
||||
{
|
||||
typedef R type(Args...) &;
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(const Disposition&, Args...) &>
|
||||
{
|
||||
typedef R type(Args...) &;
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(Disposition, Args...) &&>
|
||||
{
|
||||
typedef R type(Args...) &&;
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(const Disposition&, Args...) &&>
|
||||
{
|
||||
typedef R type(Args...) &&;
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(Disposition, Args...) noexcept>
|
||||
{
|
||||
typedef R type(Args...) & noexcept;
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(const Disposition&, Args...) noexcept>
|
||||
{
|
||||
typedef R type(Args...) & noexcept;
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(Disposition, Args...) & noexcept>
|
||||
{
|
||||
typedef R type(Args...) & noexcept;
|
||||
};
|
||||
|
||||
template <typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(const Disposition&, Args...) & noexcept>
|
||||
{
|
||||
typedef R type(Args...) & noexcept;
|
||||
};
|
||||
|
||||
template<typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(Disposition, Args...) && noexcept>
|
||||
{
|
||||
typedef R type(Args...) && noexcept;
|
||||
};
|
||||
|
||||
template <typename R, boost::asio::disposition Disposition, typename... Args>
|
||||
struct redirect_error_signature<R(const Disposition&, Args...) && noexcept>
|
||||
{
|
||||
typedef R type(Args...) && noexcept;
|
||||
};
|
||||
|
||||
template<typename Initiation, typename = void>
|
||||
class initiation_base : public Initiation
|
||||
{
|
||||
public:
|
||||
template<typename I>
|
||||
explicit initiation_base(I&& initiation)
|
||||
: Initiation(std::forward<I>(initiation)) {}
|
||||
};
|
||||
|
||||
template<typename Initiation>
|
||||
class initiation_base<Initiation,
|
||||
std::enable_if_t<!std::is_class_v<Initiation>>>
|
||||
{
|
||||
public:
|
||||
template<typename I>
|
||||
explicit initiation_base(I&& initiation)
|
||||
: initiation(std::forward<I>(initiation)) {}
|
||||
|
||||
template<typename... Args>
|
||||
void operator()(Args&&... args) const
|
||||
{
|
||||
initiation(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
Initiation initiation;
|
||||
};
|
||||
} // namespace detail
|
||||
} // namespace ceph::async
|
||||
|
||||
namespace boost::asio {
|
||||
|
||||
template<boost::asio::disposition Disposition, typename CompletionToken,
|
||||
typename Signature>
|
||||
struct async_result<::ceph::async::redirect_error_t<CompletionToken,
|
||||
Disposition>, Signature>
|
||||
: async_result<CompletionToken,
|
||||
typename ::ceph::async::detail::redirect_error_signature<
|
||||
Signature>::type>
|
||||
{
|
||||
template<typename Initiation>
|
||||
struct init_wrapper : ::ceph::async::detail::initiation_base<Initiation>
|
||||
{
|
||||
using ::ceph::async::detail::initiation_base<Initiation>::initiation_base;
|
||||
|
||||
template<typename Handler, typename... Args>
|
||||
void operator ()(Handler&& handler, Disposition* d, Args&&... args) &&
|
||||
{
|
||||
static_cast<Initiation&&>(*this)(
|
||||
::ceph::async::detail::redirect_error_handler<
|
||||
decay_t<Handler>, Disposition>(
|
||||
*d, std::forward<Handler>(handler)),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template<typename Handler, typename... Args>
|
||||
void operator ()(Handler&& handler, Disposition* d, Args&&... args) const &
|
||||
{
|
||||
static_cast<const Initiation&>(*this)(
|
||||
::ceph::async::detail::redirect_error_handler<
|
||||
decay_t<Handler>, Disposition>(
|
||||
*d, std::forward<Handler>(handler)),
|
||||
std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Initiation, typename RawCompletionToken, typename... Args>
|
||||
static auto initiate(Initiation&& initiation,
|
||||
RawCompletionToken&& token, Args&&... args)
|
||||
{
|
||||
return async_initiate<
|
||||
std::conditional_t<
|
||||
std::is_const_v<remove_reference_t<RawCompletionToken>>,
|
||||
const CompletionToken, CompletionToken>,
|
||||
typename ::ceph::async::detail::redirect_error_signature<Signature>::type>(
|
||||
init_wrapper<std::decay_t<Initiation>>(
|
||||
std::forward<Initiation>(initiation)),
|
||||
token.token, &token.disposition, std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
|
||||
template<template<typename, typename> class Associator, typename Handler,
|
||||
typename DefaultCandidate, typename Disposition>
|
||||
struct associator<Associator,
|
||||
::ceph::async::detail::redirect_error_handler<Handler,
|
||||
Disposition>,
|
||||
DefaultCandidate>
|
||||
: Associator<Handler, DefaultCandidate>
|
||||
{
|
||||
static auto get(const ::ceph::async::detail::redirect_error_handler<
|
||||
Handler, Disposition>& h) noexcept
|
||||
{
|
||||
return Associator<Handler, DefaultCandidate>::get(h.handler);
|
||||
}
|
||||
|
||||
static auto get(const ::ceph::async::detail::redirect_error_handler<
|
||||
Handler, Disposition>& h,
|
||||
const DefaultCandidate& c) noexcept
|
||||
{
|
||||
return Associator<Handler, DefaultCandidate>::get(h.handler, c);
|
||||
}
|
||||
};
|
||||
|
||||
template<boost::asio::disposition Disposition, typename... Signatures>
|
||||
struct async_result<::ceph::async::partial_redirect_error<Disposition>,
|
||||
Signatures...>
|
||||
{
|
||||
template <typename Initiation, typename RawCompletionToken, typename... Args>
|
||||
static auto initiate(Initiation&& initiation,
|
||||
RawCompletionToken&& token, Args&&... args)
|
||||
{
|
||||
return async_initiate<Signatures...>(
|
||||
std::forward<Initiation>(initiation),
|
||||
::ceph::async::redirect_error_t<
|
||||
default_completion_token_t<associated_executor_t<Initiation>>,
|
||||
Disposition>(
|
||||
default_completion_token_t<associated_executor_t<Initiation>>{},
|
||||
token.disposition), std::forward<Args>(args)...);
|
||||
}
|
||||
};
|
||||
} // namespace boost::asio
|
||||
@ -506,3 +506,6 @@ add_executable(unittest_async_cond test_async_cond.cc)
|
||||
target_link_libraries(unittest_async_cond GTest::GTest)
|
||||
add_ceph_unittest(unittest_async_cond)
|
||||
|
||||
add_executable(unittest_async_redirect_error test_async_redirect_error.cc)
|
||||
target_link_libraries(unittest_async_redirect_error GTest::GTest)
|
||||
add_ceph_unittest(unittest_async_redirect_error)
|
||||
|
||||
276
src/test/common/test_async_redirect_error.cc
Normal file
276
src/test/common/test_async_redirect_error.cc
Normal file
@ -0,0 +1,276 @@
|
||||
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
||||
// vim: ts=8 sw=2 smarttab ft=cpp
|
||||
/*
|
||||
* Ceph - scalable distributed file system
|
||||
*/
|
||||
|
||||
// Based on libs/asio/test/redirect_error.cpp which is
|
||||
// Copyright (c) 2003-2024 Christopher M. Kohlhoff (chris at kohlhoff dot com)
|
||||
//
|
||||
// Distributed under the Boost Software License, Version 1.0. (See
|
||||
// accompanying copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#include "common/async/redirect_error.h"
|
||||
|
||||
#include <exception>
|
||||
#include <future>
|
||||
|
||||
#include <boost/asio/awaitable.hpp>
|
||||
#include <boost/asio/bind_executor.hpp>
|
||||
#include <boost/asio/co_spawn.hpp>
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/system_timer.hpp>
|
||||
#include <boost/asio/use_future.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
struct redirect_error_handler
|
||||
{
|
||||
int* count;
|
||||
|
||||
explicit redirect_error_handler(int* c)
|
||||
: count(c) {}
|
||||
|
||||
void operator ()()
|
||||
{
|
||||
++(*count);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(RedirectError, RedirectError)
|
||||
{
|
||||
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(
|
||||
ceph::async::redirect_error(
|
||||
boost::asio::bind_executor(io2.get_executor(),
|
||||
redirect_error_handler(&count)), ec));
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(0, count);
|
||||
|
||||
io1.run();
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(0, count);
|
||||
|
||||
io2.run();
|
||||
|
||||
ASSERT_FALSE(ec);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
ec = boost::asio::error::would_block;
|
||||
timer1.async_wait(
|
||||
ceph::async::redirect_error(
|
||||
boost::asio::bind_executor(io2.get_executor(),
|
||||
boost::asio::deferred), ec))(redirect_error_handler(&count));
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
io1.restart();
|
||||
io1.run();
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
io2.restart();
|
||||
io2.run();
|
||||
|
||||
ASSERT_FALSE(ec);
|
||||
ASSERT_EQ(2, count);
|
||||
|
||||
ec = boost::asio::error::would_block;
|
||||
std::future<void> f = timer1.async_wait(
|
||||
ceph::async::redirect_error(
|
||||
boost::asio::bind_executor(io2.get_executor(),
|
||||
boost::asio::use_future), ec));
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(std::future_status::timeout, f.wait_for(std::chrono::seconds(0)));
|
||||
|
||||
io1.restart();
|
||||
io1.run();
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(std::future_status::timeout, f.wait_for(std::chrono::seconds(0)));
|
||||
|
||||
io2.restart();
|
||||
io2.run();
|
||||
|
||||
ASSERT_FALSE(ec);
|
||||
ASSERT_EQ(std::future_status::ready, f.wait_for(std::chrono::seconds(0)));
|
||||
}
|
||||
|
||||
TEST(RedirectError, RedirectErrorExceptionPtr)
|
||||
{
|
||||
boost::asio::io_context io;
|
||||
std::exception_ptr eptr = nullptr;
|
||||
|
||||
int count = 0;
|
||||
|
||||
boost::asio::co_spawn(
|
||||
io, []() -> boost::asio::awaitable<void> {
|
||||
throw std::exception{};
|
||||
co_return;
|
||||
},
|
||||
ceph::async::redirect_error(
|
||||
boost::asio::bind_executor(io.get_executor(),
|
||||
redirect_error_handler(&count)), eptr));
|
||||
|
||||
ASSERT_FALSE(eptr);
|
||||
ASSERT_EQ(0, count);
|
||||
|
||||
io.run();
|
||||
|
||||
ASSERT_TRUE(eptr);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
boost::asio::co_spawn(
|
||||
io, []() -> boost::asio::awaitable<void> {
|
||||
co_return;
|
||||
},
|
||||
ceph::async::redirect_error(
|
||||
boost::asio::bind_executor(io.get_executor(),
|
||||
redirect_error_handler(&count)), eptr));
|
||||
ASSERT_TRUE(eptr);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
io.restart();
|
||||
io.run();
|
||||
|
||||
ASSERT_FALSE(eptr);
|
||||
ASSERT_EQ(2, count);
|
||||
}
|
||||
|
||||
TEST(RedirectError, PartialRedirectError)
|
||||
{
|
||||
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(ceph::async::redirect_error(ec))(
|
||||
boost::asio::bind_executor(io2.get_executor(),
|
||||
redirect_error_handler(&count)));
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(0, count);
|
||||
|
||||
io1.run();
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(0, count);
|
||||
|
||||
io2.run();
|
||||
|
||||
ASSERT_FALSE(ec);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
ec = boost::asio::error::would_block;
|
||||
timer1.async_wait(ceph::async::redirect_error(ec))(
|
||||
boost::asio::bind_executor(io2.get_executor(),
|
||||
boost::asio::deferred))(redirect_error_handler(&count));
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
io1.restart();
|
||||
io1.run();
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
io2.restart();
|
||||
io2.run();
|
||||
|
||||
ASSERT_FALSE(ec);
|
||||
ASSERT_EQ(2, count);
|
||||
|
||||
ec = boost::asio::error::would_block;
|
||||
timer1.async_wait()(ceph::async::redirect_error(ec))(
|
||||
boost::asio::bind_executor(io2.get_executor(),
|
||||
boost::asio::deferred))(redirect_error_handler(&count));
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(2, count);
|
||||
|
||||
io1.restart();
|
||||
io1.run();
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
ASSERT_EQ(2, count);
|
||||
|
||||
io2.restart();
|
||||
io2.run();
|
||||
|
||||
ASSERT_FALSE(ec);
|
||||
ASSERT_EQ(3, count);
|
||||
|
||||
ec = boost::asio::error::would_block;
|
||||
std::future<void> f = timer1.async_wait(ceph::async::redirect_error(ec))(
|
||||
boost::asio::bind_executor(io2.get_executor(), boost::asio::use_future));
|
||||
|
||||
ASSERT_EQ(boost::asio::error::would_block, ec);
|
||||
EXPECT_EQ(std::future_status::timeout, f.wait_for(std::chrono::seconds(0)));
|
||||
|
||||
io1.restart();
|
||||
io1.run();
|
||||
|
||||
EXPECT_EQ(boost::asio::error::would_block, ec);
|
||||
EXPECT_EQ(std::future_status::timeout, f.wait_for(std::chrono::seconds(0)));
|
||||
|
||||
io2.restart();
|
||||
io2.run();
|
||||
|
||||
EXPECT_FALSE(ec);
|
||||
EXPECT_EQ(std::future_status::ready, f.wait_for(std::chrono::seconds(0)));
|
||||
}
|
||||
|
||||
TEST(RedirectError, PartialRedirectErrorExceptionPtr)
|
||||
{
|
||||
boost::asio::io_context io;
|
||||
std::exception_ptr eptr = nullptr;
|
||||
|
||||
int count = 0;
|
||||
|
||||
boost::asio::co_spawn(
|
||||
io, []() -> boost::asio::awaitable<void> {
|
||||
throw std::exception{};
|
||||
co_return;
|
||||
},
|
||||
ceph::async::redirect_error(eptr)(boost::asio::bind_executor(
|
||||
io.get_executor(),
|
||||
redirect_error_handler(&count))));
|
||||
|
||||
ASSERT_FALSE(eptr);
|
||||
ASSERT_EQ(0, count);
|
||||
|
||||
io.run();
|
||||
|
||||
ASSERT_TRUE(eptr);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
boost::asio::co_spawn(
|
||||
io, []() -> boost::asio::awaitable<void> {
|
||||
co_return;
|
||||
},
|
||||
ceph::async::redirect_error(eptr)(boost::asio::bind_executor(
|
||||
io.get_executor(),
|
||||
redirect_error_handler(&count))));
|
||||
ASSERT_TRUE(eptr);
|
||||
ASSERT_EQ(1, count);
|
||||
|
||||
io.restart();
|
||||
io.run();
|
||||
|
||||
ASSERT_FALSE(eptr);
|
||||
ASSERT_EQ(2, count);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user