rippled
Loading...
Searching...
No Matches
test/csf/Scheduler.h
1//------------------------------------------------------------------------------
2/*
3 This file is part of rippled: https://github.com/ripple/rippled
4 Copyright (c) 2012-2017 Ripple Labs Inc.
5
6 Permission to use, copy, modify, and/or distribute this software for any
7 purpose with or without fee is hereby granted, provided that the above
8 copyright notice and this permission notice appear in all copies.
9
10 THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 ANY SPECIAL , DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15 ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16 OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17*/
18//==============================================================================
19
20#ifndef RIPPLE_TEST_CSF_SCHEDULER_H_INCLUDED
21#define RIPPLE_TEST_CSF_SCHEDULER_H_INCLUDED
22
23#include <xrpl/basics/ByteUtilities.h>
24#include <xrpl/beast/clock/manual_clock.h>
25
26#include <boost/container/pmr/monotonic_buffer_resource.hpp>
27#include <boost/intrusive/set.hpp>
28
29#include <type_traits>
30#include <utility>
31
32namespace ripple {
33namespace test {
34namespace csf {
35
48{
49public:
51
53
55
56private:
57 using by_when_hook = boost::intrusive::set_base_hook<
58 boost::intrusive::link_mode<boost::intrusive::normal_link>>;
59
61 {
63
64 event(event const&) = delete;
65 event&
66 operator=(event const&) = delete;
67
68 virtual ~event() = default;
69
70 // Called to perform the event
71 virtual void
72 operator()() const = 0;
73
74 event(time_point when_) : when(when_)
75 {
76 }
77
78 bool
79 operator<(event const& other) const
80 {
81 return when < other.when;
82 }
83 };
84
85 template <class Handler>
86 class event_impl : public event
87 {
88 Handler const h_;
89
90 public:
91 event_impl(event_impl const&) = delete;
92
94 operator=(event_impl const&) = delete;
95
96 template <class DeducedHandler>
97 event_impl(time_point when_, DeducedHandler&& h)
98 : event(when_), h_(std::forward<DeducedHandler>(h))
99 {
100 }
101
102 void
103 operator()() const override
104 {
105 h_();
106 }
107 };
108
110 {
111 private:
112 using by_when_set = typename boost::intrusive::make_multiset<
113 event,
114 boost::intrusive::constant_time_size<false>>::type;
115 // alloc_ is owned by the scheduler
116 boost::container::pmr::monotonic_buffer_resource* alloc_;
118
119 public:
120 using iterator = typename by_when_set::iterator;
121
122 queue_type(queue_type const&) = delete;
124 operator=(queue_type const&) = delete;
125
126 explicit queue_type(
127 boost::container::pmr::monotonic_buffer_resource* alloc);
128
129 ~queue_type();
130
131 bool
132 empty() const;
133
135 begin();
136
138 end();
139
140 template <class Handler>
141 typename by_when_set::iterator
142 emplace(time_point when, Handler&& h);
143
145 erase(iterator iter);
146 };
147
148 boost::container::pmr::monotonic_buffer_resource alloc_{kilobytes(256)};
150
151 // Aged containers that rely on this clock take a non-const reference =(
153
154public:
155 Scheduler(Scheduler const&) = delete;
156 Scheduler&
157 operator=(Scheduler const&) = delete;
158
159 Scheduler();
160
163 clock() const;
164
170 now() const;
171
172 // Used to cancel timers
173 struct cancel_token;
174
183 template <class Function>
185 at(time_point const& when, Function&& f);
186
195 template <class Function>
197 in(duration const& delay, Function&& f);
198
206 void
207 cancel(cancel_token const& token);
208
218 bool
219 step_one();
220
230 bool
231 step();
232
246 template <class Function>
247 bool
248 step_while(Function&& func);
249
259 bool
260 step_until(time_point const& until);
261
271 template <class Period, class Rep>
272 bool
274};
275
276//------------------------------------------------------------------------------
277
279 boost::container::pmr::monotonic_buffer_resource* alloc)
280 : alloc_(alloc)
281{
282}
283
285{
286 for (auto iter = by_when_.begin(); iter != by_when_.end();)
287 {
288 auto e = &*iter;
289 ++iter;
290 e->~event();
291 alloc_->deallocate(e, sizeof(e));
292 }
293}
294
295inline bool
297{
298 return by_when_.empty();
299}
300
301inline auto
303{
304 return by_when_.begin();
305}
306
307inline auto
309{
310 return by_when_.end();
311}
312
313template <class Handler>
314inline auto
316 typename by_when_set::iterator
317{
318 using event_type = event_impl<std::decay_t<Handler>>;
319 auto const p = alloc_->allocate(sizeof(event_type));
320 auto& e = *new (p) event_type(when, std::forward<Handler>(h));
321 return by_when_.insert(e);
322}
323
324inline auto
325Scheduler::queue_type::erase(iterator iter) -> typename by_when_set::iterator
326{
327 auto& e = *iter;
328 auto next = by_when_.erase(iter);
329 e.~event();
330 alloc_->deallocate(&e, sizeof(e));
331 return next;
332}
333
334//-----------------------------------------------------------------------------
336{
337private:
339
340public:
341 cancel_token() = delete;
342 cancel_token(cancel_token const&) = default;
344 operator=(cancel_token const&) = default;
345
346private:
347 friend class Scheduler;
348 cancel_token(typename queue_type::iterator iter) : iter_(iter)
349 {
350 }
351};
352
353//------------------------------------------------------------------------------
355{
356}
357
358inline auto
360{
361 return clock_;
362}
363
364inline auto
366{
367 return clock_.now();
368}
369
370template <class Function>
371inline auto
372Scheduler::at(time_point const& when, Function&& f) -> cancel_token
373{
374 return queue_.emplace(when, std::forward<Function>(f));
375}
376
377template <class Function>
378inline auto
379Scheduler::in(duration const& delay, Function&& f) -> cancel_token
380{
381 return at(clock_.now() + delay, std::forward<Function>(f));
382}
383
384inline void
386{
387 queue_.erase(token.iter_);
388}
389
390inline bool
392{
393 if (queue_.empty())
394 return false;
395 auto const iter = queue_.begin();
396 clock_.set(iter->when);
397 (*iter)();
398 queue_.erase(iter);
399 return true;
400}
401
402inline bool
404{
405 if (!step_one())
406 return false;
407 for (;;)
408 if (!step_one())
409 break;
410 return true;
411}
412
413template <class Function>
414inline bool
416{
417 bool ran = false;
418 while (f() && step_one())
419 ran = true;
420 return ran;
421}
422
423inline bool
425{
426 // VFALCO This routine needs optimizing
427 if (queue_.empty())
428 {
429 clock_.set(until);
430 return false;
431 }
432 auto iter = queue_.begin();
433 if (iter->when > until)
434 {
435 clock_.set(until);
436 return true;
437 }
438 do
439 {
440 step_one();
441 iter = queue_.begin();
442 } while (iter != queue_.end() && iter->when <= until);
443 clock_.set(until);
444 return iter != queue_.end();
445}
446
447template <class Period, class Rep>
448inline bool
450{
451 return step_until(now() + amount);
452}
453
454} // namespace csf
455} // namespace test
456} // namespace ripple
457
458#endif
typename Clock::time_point time_point
typename Clock::duration duration
time_point now() const override
Returns the current time.
void set(time_point const &when)
Set the current time of the manual clock.
event_impl(time_point when_, DeducedHandler &&h)
event_impl & operator=(event_impl const &)=delete
event_impl(event_impl const &)=delete
typename boost::intrusive::make_multiset< event, boost::intrusive::constant_time_size< false > >::type by_when_set
queue_type(queue_type const &)=delete
typename by_when_set::iterator iterator
by_when_set::iterator emplace(time_point when, Handler &&h)
queue_type & operator=(queue_type const &)=delete
boost::container::pmr::monotonic_buffer_resource * alloc_
Simulated discrete-event scheduler.
Scheduler & operator=(Scheduler const &)=delete
bool step_one()
Run the scheduler for up to one event.
bool step_while(Function &&func)
Run the scheduler while a condition is true.
typename clock_type::duration duration
cancel_token at(time_point const &when, Function &&f)
Schedule an event at a specific time.
typename clock_type::time_point time_point
bool step_for(std::chrono::duration< Period, Rep > const &amount)
Run the scheduler until time has elapsed.
boost::container::pmr::monotonic_buffer_resource alloc_
time_point now() const
Return the current network time.
bool step_until(time_point const &until)
Run the scheduler until the specified time.
clock_type & clock() const
Return the clock.
boost::intrusive::set_base_hook< boost::intrusive::link_mode< boost::intrusive::normal_link > > by_when_hook
Scheduler(Scheduler const &)=delete
void cancel(cancel_token const &token)
Cancel a timer.
bool step()
Run the scheduler until no events remain.
cancel_token in(duration const &delay, Function &&f)
Schedule an event after a specified duration passes.
T is_same_v
Use hash_* containers for keys that do not need a cryptographically secure hashing algorithm.
Definition algorithm.h:25
constexpr auto kilobytes(T value) noexcept
STL namespace.
cancel_token & operator=(cancel_token const &)=default
cancel_token(typename queue_type::iterator iter)
cancel_token(cancel_token const &)=default
event & operator=(event const &)=delete
virtual void operator()() const =0
bool operator<(event const &other) const