boost.asio:如何取消可等待而不导致终止?

问题描述 投票:0回答:1

考虑以下代码:

#include <boost/asio/awaitable.hpp>
#include <boost/asio/bind_cancellation_slot.hpp>
#include <boost/asio/cancellation_signal.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/use_awaitable.hpp>

namespace ba = boost::asio;

ba::cancellation_signal cancel_sub;
void subscribe(ba::io_context &context)
{
  ba::co_spawn(
    context,
    []() -> ba::awaitable<void> { co_return; },
    ba::bind_cancellation_slot(cancel_sub.slot(), ba::detached));
  cancel_sub.emit(ba::cancellation_type::all);
}

int main()
{
  ba::io_context ctx;
  subscribe(ctx);
  ctx.run();
  return 0;
}

它使我的程序退出,并从 boost 中的 co_await 实现的内部抛出异常:

terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
  what():  co_await: Operation canceled [system:125]

如何避免这种情况?

我尝试过:

  • 在协程内禁用取消异常:
co_await ba::this_coro::throw_if_cancelled(false);
  • 在我的协程的开头添加此代码以启用各种取消:
co_await ba::this_coro::reset_cancellation_state(ba::enable_total_cancellation());

没有任何变化,我的应用程序仍然退出。

准确地说,在boost的代码中:(用

// !!! 
标记我的评论)


template <typename Handler, typename Executor, typename Function>
awaitable<awaitable_thread_entry_point, Executor> co_spawn_entry_point(
    awaitable<void, Executor>*, co_spawn_state<Handler, Executor, Function> s)
{
  (void) co_await co_spawn_dispatch{};

  (co_await awaitable_thread_has_context_switched{}) = false;
  std::exception_ptr e = nullptr;
  try
  {
    // !!! here an exception is thrown as the cancellation is observered.
    co_await s.function();
  }
  catch (...)
  {
    // !!! caught here, all is fine
    e = std::current_exception();
  }

  bool switched = (co_await awaitable_thread_has_context_switched{});
  if (!switched)
    (void) co_await co_spawn_post(); // !!! exception thrown again here as the cancellation state is checked again in await_transform! But now there is nothing to catch it...

  (dispatch)(s.handler_work.get_executor(),
      [handler = std::move(s.handler), e]() mutable
      {
        std::move(handler)(e);
      });
}
c++ boost-asio cancellation c++-coroutine
1个回答
0
投票

没有任何变化,我的应用程序仍然退出。

如果您遇到异常,但不希望应用程序退出,如何将原始协程逻辑包装在 try-catch 块中来处理所述异常,并防止其传播到协程之外?

它可以工作,因为您面临的问题与取消操作引发的异常有关,该异常会导致异常,如果不处理该异常,则会导致您的程序终止,而不是由

return_value
return_void
(与协程相关的异常) return 机制),如 Raymond Chen 的文章“C++ 协程:如果我的 return_value 中发生异常会发生什么?”中讨论。

void subscribe(ba::io_context& context) {
    ba::co_spawn(context, []() -> ba::awaitable<void> {
        try {
            // Your original coroutine logic
            co_return;
        } catch (const boost::system::system_error& e) {
            // Handle cancellation or other system errors
            // For example, log the error and continue without rethrowing
            std::cerr << "Coroutine canceled or failed: " << e.what() << std::endl;
            // You can also handle or log the cancellation specifically
            if (e.code() == boost::asio::error::operation_aborted) {
                std::cerr << "Operation was explicitly canceled.\n";
            }
        }
    }, ba::bind_cancellation_slot(cancel_sub.slot(), ba::detached));
    cancel_sub.emit(ba::cancellation_type::all);
}
© www.soinside.com 2019 - 2024. All rights reserved.