hmbdc
simplify-high-performance-messaging-programming
Timers.hpp
1 #include "hmbdc/Copyright.hpp"
2 #pragma once
3 
4 #include "hmbdc/time/Time.hpp"
5 #include "hmbdc/Exception.hpp"
6 #include "hmbdc/Compile.hpp"
7 
8 #include <functional>
9 #include <algorithm>
10 
11 #include <stdexcept>
12 #include <ctime>
13 
14 #include <boost/intrusive/set.hpp>
15 
16 
17 namespace hmbdc { namespace time {
18 struct TimerManager;
19 namespace timers_detail {
20  void noop(TimerManager&, SysTime const&);
21 }
22 
23 struct Timer
24 : boost::intrusive::set_base_hook<
25  boost::intrusive::link_mode<boost::intrusive::normal_link>
26  //the default safe_link appears too strict since many times the object is deleted
27  //before the container(TimerManager) is deleted and assert fires unnecesarily in that case
28 > {
29 protected:
30  SysTime getFireAt() const { return fireAt_; }
31 
32 public:
33  using Callback = std::function<void (TimerManager&, SysTime const&)>;
34  Timer(Callback cb = timers_detail::noop)
35  : armed(false)
36  , callback_(cb)
37  {}
38  void setCallback(Callback cb) {
39  callback_ = cb;
40  }
41  bool operator < (Timer const& other) const
42  { return fireAt_ < other.fireAt_; }
43  bool operator <= (SysTime const& t) const
44  { return !(t < fireAt_); }
45 
46  bool scheduled() const {
47  return armed;
48  }
49 
50  virtual ~Timer(){}
51 
52 private:
53  friend struct TimerManager;
54  void fired(TimerManager& tm, SysTime const& now) {
55  callback_(tm, now);
56  }
57  bool armed;
58  SysTime fireAt_;
59  Callback callback_;
60  virtual void reschedule(TimerManager& tm, SysTime const& now) = 0;
61 };
62 
63 namespace timers_detail {
64 inline
65 void noop(TimerManager&, SysTime const&)
66 {}
67 
68 } //timers_detail
69 
70 struct TimerManager {
71  /**
72  * @brief schedule the timer to start at a specific time
73  * @details make sure the Timer is not already scheduled
74  * otherwise undefined behavior
75  *
76  * @param fireAt the firing time
77  * @param timer the timer to fire
78  */
79  void schedule(SysTime fireAt, Timer& timer) {
80  timer.fireAt_ = fireAt;
81  timers_.insert(timer);
82  timer.armed = true;
83  }
84  /**
85  * @brief cancel a timer previously scheduled with the TimerManager
86  * @details if not scheduled, no effect
87  *
88  * @param timer to be canceled
89  */
90  void cancel(Timer& timer) {
91  auto range = timers_.equal_range(timer);
92  for (auto it = range.first; it != range.second; ++it) {
93  if (&*it == &timer) {
94  timers_.erase(it);
95  timer.armed = false;
96  return;
97  }
98  }
99  }
100 
101  void checkTimers(time::SysTime);
102 
103  Duration untilNextFire() const {
104  auto res = std::numeric_limits<Duration>::max();
105  if (timers_.begin() != timers_.end()) {
106  res = std::max(Duration(0), timers_.begin()->fireAt_ - SysTime::now());
107  }
108  return res;
109  }
110 
111 private:
112  using Timers = boost::intrusive::multiset<Timer>;
113  Timers timers_;
114 };
115 
116 
118  ReoccuringTimer(Duration const& interval, Callback callback = timers_detail::noop)
119  : Timer(callback)
120  , interval_(interval)
121  {}
122  void resetInterval(Duration d) {
123  interval_ = d;
124  }
125 private:
126  Duration interval_;
127  virtual void reschedule(TimerManager& tm, SysTime const& now)
128  { tm.schedule(now + interval_, *this); };
129 };
130 
131 struct DailyTimer : Timer {
132  DailyTimer(Callback callback = timers_detail::noop)
133  : Timer(callback)
134  {}
135 private:
136  virtual void reschedule(TimerManager& tm, SysTime const& now) {
137  Duration day = Duration::seconds(86400);
138  SysTime newFireTime = getFireAt() + day;
139  while (newFireTime < now) newFireTime += day;
140  tm.schedule(newFireTime, *this);
141  };
142 };
143 
144 struct OneTimeTimer : Timer {
145  OneTimeTimer(Callback callback = timers_detail::noop)
146  : Timer(callback)
147  {}
148 
149 private:
150  virtual void reschedule(TimerManager&, SysTime const&){};
151 };
152 
153 inline
154 void
155 TimerManager::
156 checkTimers(time::SysTime now) HMBDC_RESTRICT {
157  for (auto it = timers_.begin(); it != timers_.end() && hmbdc_unlikely(*it <= now);) {
158  if (hmbdc_unlikely((*it).armed)) {
159  (*it).armed = false;
160  (*it).fired(*this, now); //this could change timers_
161  it = timers_.begin();
162  } else {
163  it++;
164  }
165  }
166 
167  for (auto it = timers_.begin(); it != timers_.end() && hmbdc_unlikely(*it <= now);) {
168  auto tmp = it++;
169  if (!(*tmp).armed) {
170  timers_.erase(tmp);
171  (*tmp).reschedule(*this, now);
172  } else {
173  it++;
174  }
175  }
176 }
177 
178 }}
Definition: Timers.hpp:23
Definition: Timers.hpp:70
Definition: Timers.hpp:131
void cancel(Timer &timer)
cancel a timer previously scheduled with the TimerManager
Definition: Timers.hpp:90
Definition: Time.hpp:14
Definition: Timers.hpp:144
void schedule(SysTime fireAt, Timer &timer)
schedule the timer to start at a specific time
Definition: Timers.hpp:79
Definition: Time.hpp:134
Definition: Base.hpp:12
Definition: Timers.hpp:117