Consider the following code:
#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;
}
It makes my program exit with an exception thrown from the innards of the co_await implementation in boost:
terminate called after throwing an instance of 'boost::wrapexcept<boost::system::system_error>'
what(): co_await: Operation canceled [system:125]
How can I avoid this ?
I tried:
- Disabling exceptions on cancellation from within the coroutine:
co_await ba::this_coro::throw_if_cancelled(false);
- Adding this code in the beginning of my coroutine to enable all kinds of cancellation:
co_await ba::this_coro::reset_cancellation_state(ba::enable_total_cancellation());
Nothing changes, I still get my app exiting.
Precisely, in boost's code: (marking my comments with // !!! )
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);
});
}