xrpld
Loading...
Searching...
No Matches
src/tests/libxrpl/csf/Scheduler.h
1#pragma once
2
3#include <xrpl/basics/ByteUtilities.h>
4#include <xrpl/beast/clock/manual_clock.h>
5
6#include <boost/container/pmr/monotonic_buffer_resource.hpp>
7#include <boost/intrusive/set.hpp>
8
9#include <chrono>
10#include <type_traits>
11#include <utility>
12
13namespace xrpl::test::csf {
14
28{
29public:
31
33
35
36private:
38 boost::intrusive::set_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
39
41 {
43
44 Event(Event const&) = delete;
45 Event&
46 operator=(Event const&) = delete;
47
48 virtual ~Event() = default;
49
50 // Called to perform the event
51 virtual void
52 operator()() const = 0;
53
55 {
56 }
57
58 bool
59 operator<(Event const& other) const
60 {
61 return when < other.when;
62 }
63 };
64
65 template <class Handler>
66 class EventImpl : public Event
67 {
68 Handler const h_;
69
70 public:
71 EventImpl(EventImpl const&) = delete;
72
74 operator=(EventImpl const&) = delete;
75
76 template <class DeducedHandler>
77 EventImpl(time_point when, DeducedHandler&& h)
78 : Event(when), h_(std::forward<DeducedHandler>(h))
79 {
80 }
81
82 void
83 operator()() const override
84 {
85 h_();
86 }
87 };
88
90 {
91 private:
92 using by_when_set = boost::intrusive::
93 make_multiset<Event, boost::intrusive::constant_time_size<false>>::type;
94 // alloc_ is owned by the scheduler
95 boost::container::pmr::monotonic_buffer_resource* alloc_;
97
98 public:
99 using iterator = by_when_set::iterator;
100
101 QueueType(QueueType const&) = delete;
102 QueueType&
103 operator=(QueueType const&) = delete;
104
105 explicit QueueType(boost::container::pmr::monotonic_buffer_resource* alloc);
106
107 ~QueueType();
108
109 [[nodiscard]] bool
110 empty() const;
111
113 begin();
114
116 end();
117
118 template <class Handler>
119 by_when_set::iterator
120 emplace(time_point when, Handler&& h);
121
123 erase(iterator iter);
124 };
125
126 boost::container::pmr::monotonic_buffer_resource alloc_{kilobytes(256)};
128
129 // Aged containers that rely on this clock take a non-const reference =(
131
132public:
133 Scheduler(Scheduler const&) = delete;
134 Scheduler&
135 operator=(Scheduler const&) = delete;
136
137 Scheduler();
138
143 clock() const;
144
151 now() const;
152
153 // Used to cancel timers
154 struct CancelToken;
155
165 template <class Function>
167 at(time_point const& when, Function&& f);
168
178 template <class Function>
180 in(duration const& delay, Function&& f);
181
190 void
191 cancel(CancelToken const& token);
192
203 bool
204 stepOne();
205
216 bool
217 step();
218
233 template <class Function>
234 bool
235 stepWhile(Function&& func);
236
247 bool
248 stepUntil(time_point const& until);
249
260 template <class Period, class Rep>
261 bool
263};
264
265//------------------------------------------------------------------------------
266
267inline Scheduler::QueueType::QueueType(boost::container::pmr::monotonic_buffer_resource* alloc)
268 : alloc_(alloc)
269{
270}
271
273{
274 for (auto iter = byWhen_.begin(); iter != byWhen_.end();)
275 {
276 auto e = &*iter;
277 ++iter;
278 e->~Event();
279 alloc_->deallocate(e, sizeof(e)); // NOLINT(bugprone-sizeof-expression)
280 }
281}
282
283inline bool
285{
286 return byWhen_.empty();
287}
288
289inline auto
291{
292 return byWhen_.begin();
293}
294
295inline auto
297{
298 return byWhen_.end();
299}
300
301template <class Handler>
302inline auto
303Scheduler::QueueType::emplace(time_point when, Handler&& h) -> by_when_set::iterator
304{
305 using event_type = EventImpl<std::decay_t<Handler>>;
306 auto const p = alloc_->allocate(sizeof(event_type));
307 auto& e = *new (p) event_type(when, std::forward<Handler>(h));
308 return byWhen_.insert(e);
309}
310
311inline auto
312Scheduler::QueueType::erase(iterator iter) -> by_when_set::iterator
313{
314 auto& e = *iter;
315 auto next = byWhen_.erase(iter);
316 e.~Event();
317 alloc_->deallocate(&e, sizeof(e));
318 return next;
319}
320
321//-----------------------------------------------------------------------------
323{
324private:
326
327public:
328 CancelToken() = delete;
329 CancelToken(CancelToken const&) = default;
331 operator=(CancelToken const&) = default;
332
333private:
334 friend class Scheduler;
336 {
337 }
338};
339
340//------------------------------------------------------------------------------
342{
343}
344
345inline auto
347{
348 return clock_;
349}
350
351inline auto
353{
354 return clock_.now();
355}
356
357template <class Function>
358inline auto
359Scheduler::at(time_point const& when, Function&& f) -> CancelToken
360{
361 return queue_.emplace(when, std::forward<Function>(f));
362}
363
364template <class Function>
365inline auto
366Scheduler::in(duration const& delay, Function&& f) -> CancelToken
367{
368 return at(clock_.now() + delay, std::forward<Function>(f));
369}
370
371inline void
373{
374 queue_.erase(token.iter_);
375}
376
377inline bool
379{
380 if (queue_.empty())
381 return false;
382 auto const iter = queue_.begin();
383 clock_.set(iter->when);
384 (*iter)();
385 queue_.erase(iter);
386 return true;
387}
388
389inline bool
391{
392 if (!stepOne())
393 return false;
394 for (;;)
395 {
396 if (!stepOne())
397 break;
398 }
399 return true;
400}
401
402template <class Function>
403inline bool
405{
406 bool ran = false;
407 while (f() && stepOne())
408 ran = true;
409 return ran;
410}
411
412inline bool
414{
415 // VFALCO This routine needs optimizing
416 if (queue_.empty())
417 {
418 clock_.set(until);
419 return false;
420 }
421 auto iter = queue_.begin();
422 if (iter->when > until)
423 {
424 clock_.set(until);
425 return true;
426 }
427 do
428 {
429 stepOne();
430 iter = queue_.begin();
431 } while (iter != queue_.end() && iter->when <= until);
432 clock_.set(until);
433 return iter != queue_.end();
434}
435
436template <class Period, class Rep>
437inline bool
439{
440 return stepUntil(now() + amount);
441}
442
443} // namespace xrpl::test::csf
std::chrono::steady_clock::duration duration
std::chrono::steady_clock::time_point time_point
Manual clock implementation.
EventImpl(time_point when, DeducedHandler &&h)
EventImpl(EventImpl const &)=delete
EventImpl & operator=(EventImpl const &)=delete
QueueType & operator=(QueueType const &)=delete
boost::container::pmr::monotonic_buffer_resource * alloc_
by_when_set::iterator emplace(time_point when, Handler &&h)
boost::intrusive:: make_multiset< Event, boost::intrusive::constant_time_size< false > >::type by_when_set
QueueType(QueueType const &)=delete
clock_type & clock() const
Return the clock.
beast::ManualClock< std::chrono::steady_clock > clock_type
bool step()
Run the scheduler until no events remain.
CancelToken at(time_point const &when, Function &&f)
Schedule an event at a specific time.
boost::intrusive::set_base_hook< boost::intrusive::link_mode< boost::intrusive::normal_link > > by_when_hook
time_point now() const
Return the current network time.
bool stepUntil(time_point const &until)
Run the scheduler until the specified time.
void cancel(CancelToken const &token)
Cancel a timer.
CancelToken in(duration const &delay, Function &&f)
Schedule an event after a specified duration passes.
bool stepOne()
Run the scheduler for up to one event.
Scheduler & operator=(Scheduler const &)=delete
boost::container::pmr::monotonic_buffer_resource alloc_
bool stepWhile(Function &&func)
Run the scheduler while a condition is true.
Scheduler(Scheduler const &)=delete
bool stepFor(std::chrono::duration< Period, Rep > const &amount)
Run the scheduler until time has elapsed.
T forward(T... args)
STL namespace.
constexpr auto kilobytes(T value) noexcept
CancelToken(CancelToken const &)=default
CancelToken & operator=(CancelToken const &)=default
Event & operator=(Event const &)=delete
virtual void operator()() const =0
Event(Event const &)=delete