1 #ifndef DASYNQ_TIMERBASE_H_INCLUDED
2 #define DASYNQ_TIMERBASE_H_INCLUDED
6 #include "dasynq-naryheap.h"
10 // time_val provides a wrapper around struct timespec, which overloads operators appropriately.
16 using second_t = decltype(time.tv_sec);
17 using nsecond_t = decltype(time.tv_nsec);
24 time_val(const struct timespec &t)
29 time_val(second_t s, nsecond_t ns)
35 second_t seconds() const { return time.tv_sec; }
36 nsecond_t nseconds() const { return time.tv_nsec; }
38 second_t & seconds() { return time.tv_sec; }
39 nsecond_t & nseconds() { return time.tv_nsec; }
41 //void set_seconds(second_t s) { time.tv_sec = s; }
42 //void set_nseconds(nsecond_t ns) { time.tv_nsec = ns; }
43 //void dec_seconds() { time.tv_sec--; }
44 //void inc_seconds() { time.tv_sec++; }
46 operator timespec() const
52 inline time_val operator-(const time_val &t1, const time_val &t2)
55 diff.seconds() = t1.seconds() - t2.seconds();
56 if (t1.nseconds() > t2.nseconds()) {
57 diff.nseconds() = t1.nseconds() - t2.nseconds();
60 diff.nseconds() = 1000000000 - t2.nseconds() + t1.nseconds();
66 inline time_val operator+(const time_val &t1, const time_val &t2)
68 auto ns = t1.nseconds() + t2.nseconds();
69 auto s = t1.seconds() + t2.seconds();
70 static_assert(std::numeric_limits<decltype(ns)>::max() >= 2000000000, "type too small");
71 if (ns >= 1000000000) {
75 return time_val(s, ns);
78 inline time_val &operator+=(time_val &t1, const time_val &t2)
80 auto nsum = t1.nseconds() + t2.nseconds();
81 t1.seconds() = t1.seconds() + t2.seconds();
82 if (nsum >= 1000000000) {
90 inline bool operator<(const time_val &t1, const time_val &t2)
92 if (t1.seconds() < t2.seconds()) return true;
93 if (t1.seconds() == t2.seconds() && t1.nseconds() < t2.nseconds()) return true;
97 inline bool operator==(const time_val &t1, const time_val &t2)
99 return (t1.seconds() == t2.seconds() && t1.nseconds() == t2.nseconds());
102 inline bool operator<=(const time_val &t1, const time_val &t2)
104 if (t1.seconds() < t2.seconds()) return true;
105 if (t1.seconds() == t2.seconds() && t1.nseconds() <= t2.nseconds()) return true;
109 inline bool operator!=(const time_val &t1, const time_val &t2) { return !(t1 == t2); }
110 inline bool operator>(const time_val &t1, const time_val &t2) { return t2 < t1; }
111 inline bool operator>=(const time_val &t1, const time_val &t2) { return t2 <= t1; }
113 // Data corresponding to a single timer
117 time_val interval_time; // interval (if 0, one-off timer)
118 int expiry_count; // number of times expired
119 bool enabled; // whether timer reports events
122 timer_data(void *udata = nullptr) : interval_time(0,0), expiry_count(0), enabled(true), userdata(udata)
128 class compare_timespec
131 bool operator()(const struct timespec &a, const struct timespec &b)
133 if (a.tv_sec < b.tv_sec) {
137 if (a.tv_sec == b.tv_sec) {
138 return a.tv_nsec < b.tv_nsec;
145 using timer_queue_t = NaryHeap<timer_data, struct timespec, compare_timespec>;
146 using timer_handle_t = timer_queue_t::handle_t;
148 static inline void init_timer_handle(timer_handle_t &hnd) noexcept
150 timer_queue_t::init_handle(hnd);
153 static inline int divide_timespec(const struct timespec &num, const struct timespec &den, struct timespec &rem)
155 if (num.tv_sec < den.tv_sec) {
160 if (num.tv_sec == den.tv_sec) {
161 if (num.tv_nsec < den.tv_nsec) {
165 if (num.tv_sec == 0) {
167 rem.tv_nsec = num.tv_nsec % den.tv_nsec;
168 return num.tv_nsec / den.tv_nsec;
170 // num.tv_sec == den.tv_sec and both are >= 1.
171 // The result can only be 1:
173 rem.tv_nsec = num.tv_nsec - den.tv_nsec;
177 // At this point, num.tv_sec >= 1.
179 auto &r_sec = rem.tv_sec;
180 auto &r_nsec = rem.tv_nsec;
182 r_nsec = num.tv_nsec;
183 auto d_sec = den.tv_sec;
184 auto d_nsec = den.tv_nsec;
187 if (r_nsec >= d_nsec) {
191 r_nsec += (1000000000ULL - d_nsec);
195 // Check now for common case: one timer expiry with no overrun
196 if (r_sec < d_sec || (r_sec == d_sec && r_nsec < d_nsec)) {
201 int rval = 1; // we have subtracted 1*D already
203 // shift denominator until it is greater than/equal to numerator:
204 while (d_sec < r_sec) {
207 if (d_nsec >= 1000000000) {
208 d_nsec -= 1000000000;
215 if (d_sec < r_sec || (d_sec == r_sec && d_nsec <= r_nsec)) {
218 if (d_nsec > r_nsec) {
219 r_nsec += 1000000000;
227 bool low = d_sec & 1;
229 d_nsec += low ? 500000000ULL : 0;
237 template <typename Base> class timer_base : public Base
241 void process_timer_queue(timer_queue_t &queue, const struct timespec &curtime)
243 // Peek timer queue; calculate difference between current time and timeout
244 const struct timespec * timeout = &queue.get_root_priority();
245 while (timeout->tv_sec < curtime.tv_sec || (timeout->tv_sec == curtime.tv_sec &&
246 timeout->tv_nsec <= curtime.tv_nsec)) {
247 auto & thandle = queue.get_root();
248 timer_data &data = queue.node_data(thandle);
249 time_val &interval = data.interval_time;
252 if (interval.seconds() == 0 && interval.nseconds() == 0) {
253 // Non periodic timer
255 data.enabled = false;
256 int expiry_count = data.expiry_count;
257 data.expiry_count = 0;
258 Base::receiveTimerExpiry(thandle, data.userdata, expiry_count);
265 // First calculate the overrun in time:
266 time_val overrun = time_val(curtime) - time_val(*timeout);
268 // Now we have to divide the time overrun by the period to find the
269 // interval overrun. This requires a division of a value not representable
272 data.expiry_count += divide_timespec(overrun, interval, rem);
274 // new time is current time + interval - remainder:
275 time_val newtime = curtime + interval - rem;
277 queue.insert(thandle, newtime);
279 data.enabled = false;
280 int expiry_count = data.expiry_count;
281 data.expiry_count = 0;
282 Base::receiveTimerExpiry(thandle, data.userdata, expiry_count);
286 // repeat until all expired timeouts processed
287 timeout = &queue.get_root_priority();
294 #endif /* DASYNQ_TIMERBASE_H_INCLUDED */