xrpld
Loading...
Searching...
No Matches
src/test/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 <type_traits>
10#include <utility>
11
12namespace xrpl::test::csf {
13
26{
27public:
29
31
33
34private:
36 boost::intrusive::set_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
37
39 {
41
42 Event(Event const&) = delete;
43 Event&
44 operator=(Event const&) = delete;
45
46 virtual ~Event() = default;
47
48 // Called to perform the event
49 virtual void
50 operator()() const = 0;
51
53 {
54 }
55
56 bool
57 operator<(Event const& other) const
58 {
59 return when < other.when;
60 }
61 };
62
63 template <class Handler>
64 class EventImpl : public Event
65 {
66 Handler const h_;
67
68 public:
69 EventImpl(EventImpl const&) = delete;
70
72 operator=(EventImpl const&) = delete;
73
74 template <class DeducedHandler>
75 EventImpl(time_point when, DeducedHandler&& h)
76 : Event(when), h_(std::forward<DeducedHandler>(h))
77 {
78 }
79
80 void
81 operator()() const override
82 {
83 h_();
84 }
85 };
86
88 {
89 private:
90 using by_when_set = boost::intrusive::
91 make_multiset<Event, boost::intrusive::constant_time_size<false>>::type;
92 // alloc_ is owned by the scheduler
93 boost::container::pmr::monotonic_buffer_resource* alloc_;
95
96 public:
97 using iterator = by_when_set::iterator;
98
99 QueueType(QueueType const&) = delete;
100 QueueType&
101 operator=(QueueType const&) = delete;
102
103 explicit QueueType(boost::container::pmr::monotonic_buffer_resource* alloc);
104
105 ~QueueType();
106
107 [[nodiscard]] bool
108 empty() const;
109
111 begin();
112
114 end();
115
116 template <class Handler>
117 by_when_set::iterator
118 emplace(time_point when, Handler&& h);
119
121 erase(iterator iter);
122 };
123
124 boost::container::pmr::monotonic_buffer_resource alloc_{kilobytes(256)};
126
127 // Aged containers that rely on this clock take a non-const reference =(
129
130public:
131 Scheduler(Scheduler const&) = delete;
132 Scheduler&
133 operator=(Scheduler const&) = delete;
134
135 Scheduler();
136
139 clock() const;
140
146 now() const;
147
148 // Used to cancel timers
149 struct CancelToken;
150
159 template <class Function>
161 at(time_point const& when, Function&& f);
162
171 template <class Function>
173 in(duration const& delay, Function&& f);
174
182 void
183 cancel(CancelToken const& token);
184
194 bool
195 stepOne();
196
206 bool
207 step();
208
222 template <class Function>
223 bool
224 stepWhile(Function&& func);
225
235 bool
236 stepUntil(time_point const& until);
237
247 template <class Period, class Rep>
248 bool
250};
251
252//------------------------------------------------------------------------------
253
254inline Scheduler::QueueType::QueueType(boost::container::pmr::monotonic_buffer_resource* alloc)
255 : alloc_(alloc)
256{
257}
258
260{
261 for (auto iter = byWhen_.begin(); iter != byWhen_.end();)
262 {
263 auto e = &*iter;
264 ++iter;
265 e->~Event();
266 alloc_->deallocate(e, sizeof(e)); // NOLINT(bugprone-sizeof-expression)
267 }
268}
269
270inline bool
272{
273 return byWhen_.empty();
274}
275
276inline auto
278{
279 return byWhen_.begin();
280}
281
282inline auto
284{
285 return byWhen_.end();
286}
287
288template <class Handler>
289inline auto
290Scheduler::QueueType::emplace(time_point when, Handler&& h) -> by_when_set::iterator
291{
292 using event_type = EventImpl<std::decay_t<Handler>>;
293 auto const p = alloc_->allocate(sizeof(event_type));
294 auto& e = *new (p) event_type(when, std::forward<Handler>(h));
295 return byWhen_.insert(e);
296}
297
298inline auto
299Scheduler::QueueType::erase(iterator iter) -> by_when_set::iterator
300{
301 auto& e = *iter;
302 auto next = byWhen_.erase(iter);
303 e.~Event();
304 alloc_->deallocate(&e, sizeof(e));
305 return next;
306}
307
308//-----------------------------------------------------------------------------
310{
311private:
313
314public:
315 CancelToken() = delete;
316 CancelToken(CancelToken const&) = default;
318 operator=(CancelToken const&) = default;
319
320private:
321 friend class Scheduler;
323 {
324 }
325};
326
327//------------------------------------------------------------------------------
329{
330}
331
332inline auto
334{
335 return clock_;
336}
337
338inline auto
340{
341 return clock_.now();
342}
343
344template <class Function>
345inline auto
346Scheduler::at(time_point const& when, Function&& f) -> CancelToken
347{
348 return queue_.emplace(when, std::forward<Function>(f));
349}
350
351template <class Function>
352inline auto
353Scheduler::in(duration const& delay, Function&& f) -> CancelToken
354{
355 return at(clock_.now() + delay, std::forward<Function>(f));
356}
357
358inline void
360{
361 queue_.erase(token.iter_);
362}
363
364inline bool
366{
367 if (queue_.empty())
368 return false;
369 auto const iter = queue_.begin();
370 clock_.set(iter->when);
371 (*iter)();
372 queue_.erase(iter);
373 return true;
374}
375
376inline bool
378{
379 if (!stepOne())
380 return false;
381 for (;;)
382 {
383 if (!stepOne())
384 break;
385 }
386 return true;
387}
388
389template <class Function>
390inline bool
392{
393 bool ran = false;
394 while (f() && stepOne())
395 ran = true;
396 return ran;
397}
398
399inline bool
401{
402 // VFALCO This routine needs optimizing
403 if (queue_.empty())
404 {
405 clock_.set(until);
406 return false;
407 }
408 auto iter = queue_.begin();
409 if (iter->when > until)
410 {
411 clock_.set(until);
412 return true;
413 }
414 do
415 {
416 stepOne();
417 iter = queue_.begin();
418 } while (iter != queue_.end() && iter->when <= until);
419 clock_.set(until);
420 return iter != queue_.end();
421}
422
423template <class Period, class Rep>
424inline bool
426{
427 return stepUntil(now() + amount);
428}
429
430} // 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.
clock_type::time_point time_point
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
bool operator<(Event const &other) const
virtual void operator()() const =0
Event(Event const &)=delete