#pragma once #include "util/Assert.hpp" #include "util/Spawn.hpp" #include #include #include #include #include #include #include #include #include #include #include #include namespace cluster::impl { // TODO: Try to replace util::Repeat by this. https://github.com/XRPLF/clio/issues/2926 template class RepeatedTask { std::chrono::steady_clock::duration interval_; boost::asio::strand strand_; enum class State { Running, Stopped }; std::atomic state_ = State::Stopped; std::binary_semaphore semaphore_{0}; boost::asio::steady_timer timer_; public: RepeatedTask(std::chrono::steady_clock::duration interval, Context& ctx) : interval_(interval), strand_(boost::asio::make_strand(ctx)), timer_(strand_) { } ~RepeatedTask() { stop(); } template requires std::invocable or std::invocable void run(Fn&& f) { ASSERT(state_ == State::Stopped, "Can only be ran once"); state_ = State::Running; util::spawn(strand_, [this, f = std::forward(f)](boost::asio::yield_context yield) { boost::system::error_code ec; while (state_ == State::Running) { timer_.expires_after(interval_); timer_.async_wait(yield[ec]); if (ec or state_ != State::Running) break; if constexpr (std::invocable) { f(yield); } else { f(); } } semaphore_.release(); }); } void stop() { if (auto expected = State::Running; not state_.compare_exchange_strong(expected, State::Stopped)) return; // Already stopped or not started std::binary_semaphore cancelSemaphore{0}; boost::asio::post(strand_, [this, &cancelSemaphore]() { timer_.cancel(); cancelSemaphore.release(); }); cancelSemaphore.acquire(); semaphore_.acquire(); } }; } // namespace cluster::impl