boost::asio::co_spawn 不传播异常
boost::asio::co_spawn does not propagate exception
我在 boost::asio 方面涉足协程,我对异常处理感到困惑。从 examples in 文档来看,看起来任何 'fail' error_code
都变成了异常 - 所以我 希望假设 任何抛出的异常都会也被传播回 co_spawn
调用。但事实并非如此:
#define BOOST_ASIO_HAS_CO_AWAIT
#define BOOST_ASIO_HAS_STD_COROUTINE
#include <iostream>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/executor_work_guard.hpp>
namespace this_coro = boost::asio::this_coro;
boost::asio::awaitable<void> async_op()
{
std::cout << "About to throw" << std::endl;
throw std::runtime_error{"Bang!"};
}
int main()
{
auto ctx = boost::asio::io_context{};
auto guard = boost::asio::make_work_guard(ctx.get_executor());
boost::asio::co_spawn(ctx, async_op, boost::asio::detached);
ctx.run();
}
如果这是 运行 在调试器中,您可以看到抛出的异常,但它似乎只是挂起。暂停调试器显示 ctx.run()
正在等待新工作(由于 executor_work_guard
)。所以看起来 boost::asio 里面的东西已经默默地吞下了异常。
作为实验,我将异步操作切换为使用 boost::asio 库调用:
boost::asio::awaitable<void> async_op()
{
auto executor = co_await this_coro::executor;
auto socket = boost::asio::ip::tcp::socket{executor};
std::cout << "Starting resolve" << std::endl;
auto resolver = boost::asio::ip::tcp::resolver{executor};
const auto endpoints = co_await resolver.async_resolve("localhost",
"4444",
boost::asio::use_awaitable);
std::cout << "Starting connect (num endpoints: " << endpoints.size() << ")" << std::endl;
co_await boost::asio::async_connect(socket, endpoints, boost::asio::use_awaitable);
std::cout << "Exited" << std::endl;
}
我在端口 4444 上没有服务器 运行,所以这应该会立即失败 - 而且它会静静地失败。暂停调试器表明它卡在 epoll 中等待某些东西(我在 Linux)。
将 async_connect
CompletionToken
换成 boost::asio::redirect_error
表明操作失败:
co_await boost::asio::async_connect(socket,
endpoints,
boost::asio::redirect_error(boost::asio::use_awaitable, ec));
std::cout << "Exited: " << ec.message() << std::endl;
产量:
Starting resolve
Starting connect (num endpoints: 1)
Exited: Connection refused
那么我如何传播异常,并从 error_code
中创建它们,而不是 boost::asio 中的协程?
boost::asio::co_spawn
创建一个 separate thread。这意味着不会传播异常。您可以在这里阅读更多相关信息:
- Will main() catch exceptions thrown from threads?
- How can I propagate exceptions between threads?
但是 co_spawn
支持带有签名 void(std::exception_ptr, R)
的完成处理程序。在您的示例中,您使用了 boost::asio::detached
,这意味着忽略完成结果。简单地传播它 write a custom handler.
我在 boost::asio 方面涉足协程,我对异常处理感到困惑。从 examples in 文档来看,看起来任何 'fail' error_code
都变成了异常 - 所以我 希望假设 任何抛出的异常都会也被传播回 co_spawn
调用。但事实并非如此:
#define BOOST_ASIO_HAS_CO_AWAIT
#define BOOST_ASIO_HAS_STD_COROUTINE
#include <iostream>
#include <boost/asio/awaitable.hpp>
#include <boost/asio/co_spawn.hpp>
#include <boost/asio/detached.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/executor_work_guard.hpp>
namespace this_coro = boost::asio::this_coro;
boost::asio::awaitable<void> async_op()
{
std::cout << "About to throw" << std::endl;
throw std::runtime_error{"Bang!"};
}
int main()
{
auto ctx = boost::asio::io_context{};
auto guard = boost::asio::make_work_guard(ctx.get_executor());
boost::asio::co_spawn(ctx, async_op, boost::asio::detached);
ctx.run();
}
如果这是 运行 在调试器中,您可以看到抛出的异常,但它似乎只是挂起。暂停调试器显示 ctx.run()
正在等待新工作(由于 executor_work_guard
)。所以看起来 boost::asio 里面的东西已经默默地吞下了异常。
作为实验,我将异步操作切换为使用 boost::asio 库调用:
boost::asio::awaitable<void> async_op()
{
auto executor = co_await this_coro::executor;
auto socket = boost::asio::ip::tcp::socket{executor};
std::cout << "Starting resolve" << std::endl;
auto resolver = boost::asio::ip::tcp::resolver{executor};
const auto endpoints = co_await resolver.async_resolve("localhost",
"4444",
boost::asio::use_awaitable);
std::cout << "Starting connect (num endpoints: " << endpoints.size() << ")" << std::endl;
co_await boost::asio::async_connect(socket, endpoints, boost::asio::use_awaitable);
std::cout << "Exited" << std::endl;
}
我在端口 4444 上没有服务器 运行,所以这应该会立即失败 - 而且它会静静地失败。暂停调试器表明它卡在 epoll 中等待某些东西(我在 Linux)。
将 async_connect
CompletionToken
换成 boost::asio::redirect_error
表明操作失败:
co_await boost::asio::async_connect(socket,
endpoints,
boost::asio::redirect_error(boost::asio::use_awaitable, ec));
std::cout << "Exited: " << ec.message() << std::endl;
产量:
Starting resolve
Starting connect (num endpoints: 1)
Exited: Connection refused
那么我如何传播异常,并从 error_code
中创建它们,而不是 boost::asio 中的协程?
boost::asio::co_spawn
创建一个 separate thread。这意味着不会传播异常。您可以在这里阅读更多相关信息:
- Will main() catch exceptions thrown from threads?
- How can I propagate exceptions between threads?
但是 co_spawn
支持带有签名 void(std::exception_ptr, R)
的完成处理程序。在您的示例中,您使用了 boost::asio::detached
,这意味着忽略完成结果。简单地传播它 write a custom handler.