fix: Fiber suspension during exception (#3187)

This commit is contained in:
Alex Kremer
2026-08-24 15:15:26 +01:00
committed by GitHub
parent 74f5dcc95e
commit bdfbb78115
3 changed files with 57 additions and 7 deletions

View File

@@ -103,16 +103,21 @@ retryOnTimeout(
auto retry = util::makeRetryExponentialBackoff(delays, yield.get_executor());
while (true) {
// Suspending a coroutine from inside a catch block is not safe so we copy out the failure.
std::string failure;
try {
return func();
} catch (DatabaseError const& e) {
auto const delayMs =
std::chrono::duration_cast<std::chrono::milliseconds>(retry.delayValue()).count();
LOG(log.error()) << e.what() << " (attempt " << retry.attemptNumber() + 1
<< "). Retrying in " << delayMs << "ms ...";
retry.wait(yield);
failure = e.what();
}
auto const delayMs =
std::chrono::duration_cast<std::chrono::milliseconds>(retry.delayValue()).count();
LOG(log.error()) << failure << " (attempt " << retry.attemptNumber() + 1
<< "). Retrying in " << delayMs << "ms ...";
retry.wait(yield);
}
}

View File

@@ -1,5 +1,7 @@
#include "util/Retry.hpp"
#include "util/Assert.hpp"
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
@@ -8,6 +10,7 @@
#include <algorithm>
#include <chrono>
#include <cstddef>
#include <exception>
#include <memory>
#include <utility>
@@ -95,6 +98,14 @@ ExponentialBackoffStrategy::nextDelay() const
void
Retry::wait(boost::asio::yield_context yield)
{
// Suspending a coroutine while an exception is being handled corrupts the caught-exception
// state, which is per-thread rather than per-coroutine. Copy whatever is needed out of the
// handler and let it exit before waiting.
ASSERT(
std::current_exception() == nullptr,
"Retry::wait must not be called while an exception is being handled"
);
*canceled_ = false;
timer_.expires_after(strategy_->getDelay());
strategy_->increaseDelay();