1 #ifndef DASYNQ_TIMERBASE_H_INCLUDED
2 #define DASYNQ_TIMERBASE_H_INCLUDED
9 #include "dasynq-daryheap.h"
13 // time_val provides a wrapper around struct timespec, which overloads operators appropriately.
19 using second_t = decltype(time.tv_sec);
20 using nsecond_t = decltype(time.tv_nsec);
27 time_val(const struct timespec &t) noexcept
32 time_val(second_t s, nsecond_t ns) noexcept
38 second_t seconds() const noexcept{ return time.tv_sec; }
39 nsecond_t nseconds() const noexcept { return time.tv_nsec; }
41 second_t & seconds() noexcept { return time.tv_sec; }
42 nsecond_t & nseconds() noexcept { return time.tv_nsec; }
44 timespec & get_timespec() noexcept
49 const timespec & get_timespec() const noexcept
54 operator timespec() const noexcept
60 inline time_val operator-(const time_val &t1, const time_val &t2) noexcept
63 diff.seconds() = t1.seconds() - t2.seconds();
64 if (t1.nseconds() >= t2.nseconds()) {
65 diff.nseconds() = t1.nseconds() - t2.nseconds();
68 diff.nseconds() = 1000000000 - t2.nseconds() + t1.nseconds();
74 inline time_val operator+(const time_val &t1, const time_val &t2) noexcept
76 auto ns = t1.nseconds() + t2.nseconds();
77 auto s = t1.seconds() + t2.seconds();
78 static_assert(std::numeric_limits<decltype(ns)>::max() >= 2000000000, "type too small");
79 if (ns >= 1000000000) {
83 return time_val(s, ns);
86 inline time_val &operator+=(time_val &t1, const time_val &t2) noexcept
88 auto nsum = t1.nseconds() + t2.nseconds();
89 t1.seconds() = t1.seconds() + t2.seconds();
90 if (nsum >= 1000000000) {
98 inline time_val &operator-=(time_val &t1, const time_val &t2) noexcept
101 t1.seconds() = t1.seconds() - t2.seconds();
102 if (t1.nseconds() >= t2.nseconds()) {
103 t1.nseconds() = t1.nseconds() - t2.nseconds();
106 t1.nseconds() = 1000000000 - t2.nseconds() + t1.nseconds();
112 inline bool operator<(const time_val &t1, const time_val &t2) noexcept
114 if (t1.seconds() < t2.seconds()) return true;
115 if (t1.seconds() == t2.seconds() && t1.nseconds() < t2.nseconds()) return true;
119 inline bool operator==(const time_val &t1, const time_val &t2) noexcept
121 return (t1.seconds() == t2.seconds() && t1.nseconds() == t2.nseconds());
124 inline bool operator<=(const time_val &t1, const time_val &t2) noexcept
126 if (t1.seconds() < t2.seconds()) return true;
127 if (t1.seconds() == t2.seconds() && t1.nseconds() <= t2.nseconds()) return true;
131 inline bool operator!=(const time_val &t1, const time_val &t2) noexcept { return !(t1 == t2); }
132 inline bool operator>(const time_val &t1, const time_val &t2) noexcept { return t2 < t1; }
133 inline bool operator>=(const time_val &t1, const time_val &t2) noexcept { return t2 <= t1; }
135 static inline int divide_timespec(const struct timespec &num, const struct timespec &den, struct timespec &rem) noexcept;
137 inline int operator/(const time_val &t1, const time_val &t2) noexcept
139 struct timespec remainder;
140 return divide_timespec(t1.get_timespec(), t2.get_timespec(), remainder);
143 inline time_val & operator<<=(time_val &t, int n) noexcept
145 for (int i = 0; i < n; i++) {
148 if (t.nseconds() >= 1000000000) {
149 t.nseconds() -= 1000000000;
156 inline time_val operator<<(time_val &t, int n) noexcept
163 inline time_val & operator>>=(time_val &t, int n) noexcept
165 for (int i = 0; i < n; i++) {
166 bool low = t.seconds() & 1;
168 t.nseconds() += low ? 500000000ULL : 0;
174 inline time_val operator>>(time_val &t, int n) noexcept
181 // Data corresponding to a single timer
185 time_val interval_time; // interval (if 0, one-off timer)
186 int expiry_count; // number of times expired
187 bool enabled; // whether timer reports events
190 timer_data(void *udata = nullptr) noexcept : interval_time(0,0), expiry_count(0), enabled(true), userdata(udata)
196 class compare_timespec
199 bool operator()(const struct timespec &a, const struct timespec &b) noexcept
201 if (a.tv_sec < b.tv_sec) {
205 if (a.tv_sec == b.tv_sec) {
206 return a.tv_nsec < b.tv_nsec;
213 using timer_queue_t = dary_heap<timer_data, time_val, compare_timespec>;
214 using timer_handle_t = timer_queue_t::handle_t;
216 static inline void init_timer_handle(timer_handle_t &hnd) noexcept
218 timer_queue_t::init_handle(hnd);
221 static inline int divide_timespec(const struct timespec &num, const struct timespec &den, struct timespec &rem) noexcept
223 if (num.tv_sec < den.tv_sec) {
228 if (num.tv_sec == den.tv_sec) {
229 if (num.tv_nsec < den.tv_nsec) {
233 if (num.tv_sec == 0) {
235 rem.tv_nsec = num.tv_nsec % den.tv_nsec;
236 return num.tv_nsec / den.tv_nsec;
238 // num.tv_sec == den.tv_sec and both are >= 1.
239 // The result can only be 1:
241 rem.tv_nsec = num.tv_nsec - den.tv_nsec;
245 // At this point, num.tv_sec >= 1.
247 time_val n = { num.tv_sec, num.tv_nsec };
248 time_val d = { den.tv_sec, den.tv_nsec };
251 // starting with numerator, subtract 1*denominator
254 // Check now for common case: one timer expiry with no overrun
261 int rval = 1; // we have subtracted 1*D already
263 // shift denominator until it is greater than / roughly equal to numerator:
264 while (d.seconds() < r.seconds()) {
283 template <typename Base> class timer_base : public Base
286 timer_queue_t timer_queue;
288 #if defined(CLOCK_MONOTONIC)
289 timer_queue_t mono_timer_queue;
292 inline timer_queue_t &queue_for_clock(clock_type clock)
294 if (clock == clock_type::MONOTONIC) {
295 return mono_timer_queue;
302 inline bool timer_queues_empty()
304 return timer_queue.empty() && mono_timer_queue.empty();
307 // If there is no monotonic clock, map both clock_type::MONOTONIC and clock_type::SYSTEM to a
308 // single clock (based on gettimeofday).
310 inline timer_queue_t &queue_for_clock(clock_type clock)
315 inline bool timer_queues_empty()
317 return timer_queue.empty();
321 // For the specified timer queue, issue expirations for all timers set to expire on or before the given
323 void process_timer_queue(timer_queue_t &queue, const struct timespec &curtime) noexcept
325 if (queue.empty()) return;
327 // Peek timer queue; calculate difference between current time and timeout
328 const time_val * timeout = &queue.get_root_priority();
329 time_val curtime_tv = curtime;
330 while (*timeout <= curtime_tv) {
331 auto & thandle = queue.get_root();
332 timer_data &data = queue.node_data(thandle);
333 time_val &interval = data.interval_time;
336 if (interval.seconds() == 0 && interval.nseconds() == 0) {
337 // Non periodic timer
339 data.enabled = false;
340 int expiry_count = data.expiry_count;
341 data.expiry_count = 0;
342 Base::receive_timer_expiry(thandle, data.userdata, expiry_count);
349 // First calculate the overrun in time:
350 time_val overrun = time_val(curtime) - time_val(*timeout);
352 // Now we have to divide the time overrun by the period to find the
353 // interval overrun. This requires a division of a value not representable
356 data.expiry_count += divide_timespec(overrun, interval, rem);
358 // new time is current time + interval - remainder:
359 time_val newtime = curtime + interval - rem;
361 queue.insert(thandle, newtime);
363 data.enabled = false;
364 int expiry_count = data.expiry_count;
365 data.expiry_count = 0;
366 Base::receive_timer_expiry(thandle, data.userdata, expiry_count);
370 // repeat until all expired timeouts processed
371 timeout = &queue.get_root_priority();
375 // Process timers based on the current clock time. If any timers have expired,
376 // set do_wait to false; otherwise, if any timers are pending, set ts to the delay before
377 // the next timer expires and set wait_ts to &ts.
378 // (If no timers are active, none of the output parameters are set).
379 inline void process_timers(clock_type clock, bool &do_wait, timespec &ts, timespec *&wait_ts)
382 auto &timer_q = this->queue_for_clock(clock);
383 this->get_time(now, clock, true);
384 if (! timer_q.empty()) {
385 const time_val &timeout = timer_q.get_root_priority();
386 if (timeout <= now) {
387 this->process_timer_queue(timer_q, now);
388 do_wait = false; // don't wait, we have events already
391 ts = (timeout - now);
397 // Process timers based on the current clock time. If any timers have expired,
398 // set do_wait to false; otherwise, if any timers are pending, set tv to the delay before
399 // the next timer expires and set wait_tv to &tv.
400 // (If no timers are active, none of the output parameters are set).
401 inline void process_timers(clock_type clock, bool &do_wait, timeval &tv, timeval *&wait_tv)
404 auto &timer_q = this->queue_for_clock(clock);
405 this->get_time(now, clock, true);
406 if (! timer_q.empty()) {
407 const time_val &timeout = timer_q.get_root_priority();
408 if (timeout <= now) {
409 this->process_timer_queue(timer_q, now);
410 do_wait = false; // don't wait, we have events already
413 time_val delay = (timeout - now);
414 tv.tv_sec = delay.seconds();
415 tv.tv_usec = (delay.nseconds() + 999) / 1000;
421 // Process monotonic timers based on the current clock time.
422 inline void process_monotonic_timers()
425 auto &timer_q = this->queue_for_clock(clock_type::MONOTONIC);
426 this->get_time(now, clock_type::MONOTONIC, true);
427 process_timer_queue(timer_q, now);
430 // Process monotonic timers based on the current clock time. If any timers have expired,
431 // set do_wait to false; otherwise, if any timers are pending, set ts to the delay before
432 // the next timer expires and set wait_ts to &ts.
433 // (If no timers are active, none of the output parameters are set).
434 inline void process_monotonic_timers(bool &do_wait, timespec &ts, timespec *&wait_ts)
436 process_timers(clock_type::MONOTONIC, do_wait, ts, wait_ts);
439 // Process monotonic timers based on the current clock time. If any timers have expired,
440 // set do_wait to false; otherwise, if any timers are pending, set ts to the delay before
441 // the next timer expires and set wait_ts to &ts.
442 // (If no timers are active, none of the output parameters are set).
443 inline void process_monotonic_timers(bool &do_wait, timeval &tv, timeval *&wait_tv)
445 process_timers(clock_type::MONOTONIC, do_wait, tv, wait_tv);
450 void get_time(time_val &tv, clock_type clock, bool force_update) noexcept
452 get_time(tv.get_timespec(), clock, force_update);
455 #ifdef CLOCK_MONOTONIC
456 void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
458 clockid_t posix_clock_id = (clock == clock_type::MONOTONIC) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
459 clock_gettime(posix_clock_id, &ts);
462 // If CLOCK_MONOTONIC is not defined, assume we only have gettimeofday():
463 void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
465 struct timeval curtime_tv;
466 gettimeofday(&curtime_tv, nullptr);
467 ts.tv_sec = curtime_tv.tv_sec;
468 ts.tv_nsec = curtime_tv.tv_usec * 1000;
472 void add_timer_nolock(timer_handle_t &h, void *userdata, clock_type clock = clock_type::MONOTONIC)
474 this->queue_for_clock(clock).allocate(h, userdata);
477 void remove_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
479 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
480 remove_timer_nolock(timer_id, clock);
483 void remove_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
485 auto &timer_queue = this->queue_for_clock(clock);
486 if (timer_queue.is_queued(timer_id)) {
487 timer_queue.remove(timer_id);
489 timer_queue.deallocate(timer_id);
492 // Enables or disabling report of timeouts (does not stop timer)
493 void enable_timer(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
495 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
496 enable_timer_nolock(timer_id, enable, clock);
499 void enable_timer_nolock(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
501 auto &timer_queue = this->queue_for_clock(clock);
502 auto &node_data = timer_queue.node_data(timer_id);
503 auto expiry_count = node_data.expiry_count;
504 if (expiry_count != 0 && enable) {
505 node_data.expiry_count = 0;
506 Base::receive_timer_expiry(timer_id, node_data.userdata, expiry_count);
509 timer_queue.node_data(timer_id).enabled = enable;
516 #endif /* DASYNQ_TIMERBASE_H_INCLUDED */