chore: Repeat-based tests TSAN fixes (#2810)

This commit is contained in:
Alex Kremer
2025-11-25 12:15:43 +00:00
committed by GitHub
parent 1b1a46c429
commit 4eadaa85fa
3 changed files with 28 additions and 11 deletions

View File

@@ -19,6 +19,8 @@
#include "util/Repeat.hpp"
#include <boost/asio/post.hpp>
namespace util {
void
@@ -27,8 +29,11 @@ Repeat::stop()
if (control_->stopping)
return;
control_->stopping = true;
control_->timer.cancel();
boost::asio::post(control_->strand, [control = control_] {
control->stopping = true;
control->timer.cancel();
});
control_->semaphore.acquire();
}

View File

@@ -21,9 +21,11 @@
#include "util/Assert.hpp"
#include <boost/asio/any_io_executor.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/post.hpp>
#include <boost/asio/steady_timer.hpp>
#include <boost/asio/strand.hpp>
#include <atomic>
#include <chrono>
@@ -41,10 +43,11 @@ namespace util {
class Repeat {
struct Control {
boost::asio::steady_timer timer;
boost::asio::strand<boost::asio::any_io_executor> strand;
std::atomic_bool stopping{true};
std::binary_semaphore semaphore{0};
Control(auto& ctx) : timer(ctx)
Control(auto& ctx) : timer(ctx), strand(boost::asio::make_strand(ctx))
{
}
};
@@ -98,15 +101,24 @@ private:
static void
startImpl(std::shared_ptr<Control> control, std::chrono::steady_clock::duration interval, Action&& action)
{
control->timer.expires_after(interval);
control->timer.async_wait([control, interval, action = std::forward<Action>(action)](auto const& ec) mutable {
if (ec or control->stopping) {
boost::asio::post(control->strand, [control, interval, action = std::forward<Action>(action)]() mutable {
if (control->stopping) {
control->semaphore.release();
return;
}
action();
startImpl(std::move(control), interval, std::forward<Action>(action));
control->timer.expires_after(interval);
control->timer.async_wait(
[control, interval, action = std::forward<Action>(action)](auto const& ec) mutable {
if (ec or control->stopping) {
control->semaphore.release();
return;
}
action();
startImpl(std::move(control), interval, std::forward<Action>(action));
}
);
});
}
};

View File

@@ -53,16 +53,16 @@ struct RepeatTests : SyncAsioContextTest {
TEST_F(RepeatTests, CallsHandler)
{
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
EXPECT_CALL(handlerMock, Call).Times(testing::AtMost(22));
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
runContextFor(std::chrono::milliseconds{20});
}
TEST_F(RepeatTests, StopsOnStop)
{
withRunningContext([this]() {
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
EXPECT_CALL(handlerMock, Call).Times(AtLeast(1));
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
std::this_thread::sleep_for(std::chrono::milliseconds{10});
repeat.stop();
});
@@ -72,8 +72,8 @@ TEST_F(RepeatTests, RunsAfterStop)
{
withRunningContext([this]() {
for ([[maybe_unused]] auto i : std::ranges::iota_view(0, 2)) {
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
EXPECT_CALL(handlerMock, Call).Times(AtLeast(1));
repeat.start(std::chrono::milliseconds{1}, handlerMock.AsStdFunction());
std::this_thread::sleep_for(std::chrono::milliseconds{10});
repeat.stop();
}