Reputation: 5118
I'm currently investigating a crash scenario that is caused by performing a Boost yield inside a C++ catch block. Here's a minimal reproducible example that leads to a crash. Notice the following points :
Here's a standalone reproduction example. Unfortunately I wasn't to make it shorter, but you can simply run it as-is linked to boost version 1.86-1.83
#include <boost/asio.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/thread.hpp>
#include <iostream>
namespace asio = boost::asio;
using namespace std::chrono_literals;
void TimerCatchYield(asio::steady_timer& timer,
const asio::yield_context& yield) {
boost::system::error_code ec;
try {
throw std::runtime_error("");
} catch (const std::exception&) {
// async_wait(yield) here - causes crash
timer.async_wait(yield[ec]);
}
// async_wait(yield) here - works as expected
}
void coroutineA(asio::steady_timer& timer, const asio::yield_context& yield) {
std::cout << "Coroutine A: Starting timer...\n";
timer.expires_after(500ms);
TimerCatchYield(timer, yield);
std::cout << "Coroutine A: 1\n";
timer.expires_after(500ms);
TimerCatchYield(timer, yield);
std::cout << "Coroutine A: 2\n";
timer.expires_after(500ms);
TimerCatchYield(timer, yield);
std::cout << "Coroutine A: 3\n";
std::cout << "Coroutine A: all timers expired\n";
}
void coroutineB(asio::steady_timer& timer, const asio::yield_context& yield) {
std::cout << "Coroutine B: Starting timer...\n";
boost::system::error_code ec;
timer.expires_after(500ms);
timer.async_wait(yield[ec]);
std::cout << "Coroutine B: 1\n";
timer.expires_after(500ms);
timer.async_wait(yield[ec]);
std::cout << "Coroutine B: 2\n";
timer.expires_after(500ms);
timer.async_wait(yield[ec]);
std::cout << "Coroutine B: 3\n";
std::cout << "Coroutine B: all timers expired\n";
}
int main() {
asio::io_context io;
asio::steady_timer timerA(io);
asio::steady_timer timerB(io);
auto strand = make_strand(io);
boost::thread_group threads;
spawn(strand,
[&](const asio::yield_context& yield) { coroutineA(timerA, yield); });
spawn(strand, [&](const asio::yield_context& yield_b) {
coroutineB(timerB, yield_b);
});
for (int i = 0; i < 4; ++i) {
threads.create_thread([&io]() { io.run(); });
}
threads.join_all();
std::cout << "Main finished\n";
return 0;
}
Upvotes: 1
Views: 71