-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Consider the code below:
#include <boost/asio.hpp>
#include <boost/asio/experimental/awaitable_operators.hpp>
#include <boost/asio/multiple_exceptions.hpp>
#include <boost/asio/steady_timer.hpp>
#include <chrono>
#include <exception>
#include <future>
#include <iostream>
#include <stdexcept>
#include <string>
namespace asio = boost::asio;
using asio::awaitable;
using asio::co_spawn;
using asio::detached;
using asio::steady_timer;
using asio::use_awaitable;
using namespace std::chrono_literals;
using namespace asio::experimental::awaitable_operators;
struct MyError : std::exception {
const char* what() const noexcept override { return "MyError: this is the one I expected to catch"; }
};
static awaitable<void> async_sleep(std::chrono::steady_clock::duration d)
{
steady_timer t(co_await asio::this_coro::executor, d);
co_await t.async_wait(use_awaitable);
}
static awaitable<void> throw_my_error_after(std::chrono::steady_clock::duration d)
{
co_await async_sleep(d);
throw MyError{};
}
static awaitable<void> demo()
{
try {
co_await (async_sleep(5s) && throw_my_error_after(50ms));
}
catch (const MyError&) {
std::cout << "I'd like to be here\n";
}
catch (const asio::multiple_exceptions& me) {
std::cout << "I'm here, since the other coroutine is cancelled which results in operation_aborted error from it.\n";
// And even me.first_exception() is not MyError since it gets the operation_aborted error
}
co_return;
}
int main()
{
asio::io_context io;
co_spawn(io, demo(), asio::detached);
io.run();
return 0;
}
In the example above, one of the coroutines passed to && throws MyError and I would expect this one to be thrown out of the && expression. However, when the exception is thrown, the operator cancels the other coroutine (which is quite expected) which results in system_error with operation_aborted from it (which is also expected). Now, we have two exceptions so we won't catch MyError but rather asio::multiple_exceptions. Moreover, the real exception is not even accessible from multiple_exceptions.
Also, note that handling exceptions via asio::multiple_exceptions is inconvenient. In practice, we would need to write sophiticated try/catch ladders for that. It would be much easier if this operator propagated just the first catched exception and ignored the following ones.