33class ManualScheduler final :
public Scheduler {
35 ManualScheduler() =
default;
36 ~ManualScheduler()
override =
default;
37 ManualScheduler(ManualScheduler
const&) =
delete;
38 ManualScheduler& operator=(ManualScheduler
const&) =
delete;
39 ManualScheduler(ManualScheduler&&) =
delete;
40 ManualScheduler& operator=(ManualScheduler&&) =
delete;
47 [[nodiscard]]
TimerHandle after(std::chrono::milliseconds delay, std::function<
void()> fn)
override {
48 return add(std::max(delay, std::chrono::milliseconds{0}), std::chrono::milliseconds{0}, std::move(fn));
56 [[nodiscard]]
TimerHandle every(std::chrono::milliseconds period, std::function<
void()> fn)
override {
57 if (period.count() <= 0) {
58 throw std::invalid_argument{
"ManualScheduler::every: the period must be positive"};
60 return add(period, period, std::move(fn));
73 void advance(std::chrono::milliseconds delta) {
74 if (delta.count() < 0) {
75 throw std::invalid_argument{
"ManualScheduler::advance: the delta must not be negative"};
77 if (_state->advancing) {
78 throw std::logic_error{
"ManualScheduler::advance: called from a timer callback"};
82 for (Timer& timer : _state->timers) {
84 timer.deadline = _state->now;
85 timer.deferred =
false;
88 _state->advancing =
true;
90 fireUntil(_state->now + delta);
92 _state->advancing =
false;
95 _state->advancing =
false;
101 [[nodiscard]] std::chrono::milliseconds
now()
const {
return _state->now; }
106 [[nodiscard]] std::size_t
pendingTimers()
const {
return _state->timers.size(); }
111 std::chrono::milliseconds deadline;
112 std::chrono::milliseconds period;
113 std::shared_ptr<std::function<void()>
const> fn;
121 std::chrono::milliseconds
now{0};
122 std::uint64_t nextId = 0;
123 bool advancing =
false;
124 std::vector<Timer> timers;
127 void fireUntil(std::chrono::milliseconds target) {
128 auto& timers = _state->timers;
131 auto due = timers.end();
132 for (
auto timer = timers.begin(); timer != timers.end(); ++timer) {
133 if (!timer->deferred && timer->deadline <= target &&
134 (due == timers.end() ||
135 std::pair{timer->deadline, timer->id} < std::pair{due->deadline, due->id})) {
139 if (due == timers.end()) {
142 _state->now = due->deadline;
144 std::shared_ptr<std::function<void()>
const>
const fire = due->fn;
145 if (due->period.count() > 0) {
146 due->deadline += due->period;
152 _state->now = target;
155 TimerHandle add(std::chrono::milliseconds delay, std::chrono::milliseconds period, std::function<
void()> fn) {
156 auto const timerId = ++_state->nextId;
157 _state->timers.push_back(Timer{.id = timerId,
158 .deadline = _state->now +
delay,
160 .fn = std::make_shared<std::function<void()>
const>(std::move(fn)),
161 .deferred = _state->advancing &&
delay.count() <= 0});
162 auto const isTimer = [timerId](Timer
const& timer) {
return timer.id == timerId; };
163 return TimerHandle{[weak = std::weak_ptr<State>{_state}, isTimer] {
164 if (
auto const state = weak.lock()) {
165 std::erase_if(state->timers, isTimer);
168 [weak = std::weak_ptr<State>{_state}, isTimer] {
169 auto const state = weak.lock();
170 return state !=
nullptr && std::ranges::any_of(state->timers, isTimer);
174 std::shared_ptr<State> _state = std::make_shared<State>();