feat: Repeating operations for util::async (#1776)

Async framework needed a way to do repeating operations (think simplest
cases like AmendmentBlockHandler).
This PR implements the **absolute minimum**, barebones repeating
operations that
- Can't return any values (void)
- Do not take any arguments in the user-provided function
- Can't be scheduled (i.e. a delay before starting repeating)
- Can't be stopped from inside the user block of code (i.e. does not
have stop token or anything like that)
- Can be stopped through the RepeatedOperation's `abort()` function (but
not from the user-provided repeating function)
This commit is contained in:
Alex Kremer
2024-12-20 13:24:01 +00:00
committed by GitHub
parent f2a89b095d
commit 285d4e6e9b
17 changed files with 292 additions and 90 deletions

View File

@@ -17,15 +17,20 @@
*/
//==============================================================================
#include "util/Profiler.hpp"
#include "util/async/Operation.hpp"
#include "util/async/context/BasicExecutionContext.hpp"
#include "util/async/context/SyncExecutionContext.hpp"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <chrono>
#include <cstddef>
#include <semaphore>
#include <stdexcept>
#include <string>
#include <thread>
using namespace util::async;
using ::testing::Types;
@@ -36,6 +41,12 @@ template <typename T>
struct ExecutionContextTests : public ::testing::Test {
using ExecutionContextType = T;
ExecutionContextType ctx{2};
~ExecutionContextTests() override
{
ctx.stop();
ctx.join();
}
};
TYPED_TEST_CASE(ExecutionContextTests, ExecutionContextTypes);
@@ -167,6 +178,23 @@ TYPED_TEST(ExecutionContextTests, timerUnknownException)
EXPECT_TRUE(std::string{err}.ends_with("unknown"));
}
TYPED_TEST(ExecutionContextTests, repeatingOperation)
{
auto const repeatDelay = std::chrono::milliseconds{1};
auto const timeout = std::chrono::milliseconds{15};
auto callCount = 0uz;
auto res = this->ctx.executeRepeatedly(repeatDelay, [&] { ++callCount; });
auto timeSpent = util::timed([timeout] { std::this_thread::sleep_for(timeout); }); // calculate actual time spent
res.abort(); // outside of the above stopwatch because it blocks and can take arbitrary time
auto const expectedPureCalls = timeout.count() / repeatDelay.count();
auto const expectedActualCount = timeSpent / repeatDelay.count();
EXPECT_GE(callCount, expectedPureCalls / 2u); // expect at least half of the scheduled calls
EXPECT_LE(callCount, expectedActualCount); // never should be called more times than possible before timeout
}
TYPED_TEST(ExecutionContextTests, strandMove)
{
auto strand = this->ctx.makeStrand();