1 #ifndef DASYNQ_TIMERBASE_H_INCLUDED
2 #define DASYNQ_TIMERBASE_H_INCLUDED
7 #include "dasynq-daryheap.h"
11 // time_val provides a wrapper around struct timespec, which overloads operators appropriately.
17 using second_t = decltype(time.tv_sec);
18 using nsecond_t = decltype(time.tv_nsec);
25 time_val(const struct timespec &t) noexcept
30 time_val(second_t s, nsecond_t ns) noexcept
36 second_t seconds() const noexcept{ return time.tv_sec; }
37 nsecond_t nseconds() const noexcept { return time.tv_nsec; }
39 second_t & seconds() noexcept { return time.tv_sec; }
40 nsecond_t & nseconds() noexcept { return time.tv_nsec; }
42 timespec & get_timespec() noexcept
47 const timespec & get_timespec() const noexcept
52 operator timespec() const noexcept
58 inline time_val operator-(const time_val &t1, const time_val &t2) noexcept
61 diff.seconds() = t1.seconds() - t2.seconds();
62 if (t1.nseconds() >= t2.nseconds()) {
63 diff.nseconds() = t1.nseconds() - t2.nseconds();
66 diff.nseconds() = 1000000000 - t2.nseconds() + t1.nseconds();
72 inline time_val operator+(const time_val &t1, const time_val &t2) noexcept
74 auto ns = t1.nseconds() + t2.nseconds();
75 auto s = t1.seconds() + t2.seconds();
76 static_assert(std::numeric_limits<decltype(ns)>::max() >= 2000000000, "type too small");
77 if (ns >= 1000000000) {
81 return time_val(s, ns);
84 inline time_val &operator+=(time_val &t1, const time_val &t2) noexcept
86 auto nsum = t1.nseconds() + t2.nseconds();
87 t1.seconds() = t1.seconds() + t2.seconds();
88 if (nsum >= 1000000000) {
96 inline time_val &operator-=(time_val &t1, const time_val &t2) noexcept
99 t1.seconds() = t1.seconds() - t2.seconds();
100 if (t1.nseconds() >= t2.nseconds()) {
101 t1.nseconds() = t1.nseconds() - t2.nseconds();
104 t1.nseconds() = 1000000000 - t2.nseconds() + t1.nseconds();
110 inline bool operator<(const time_val &t1, const time_val &t2) noexcept
112 if (t1.seconds() < t2.seconds()) return true;
113 if (t1.seconds() == t2.seconds() && t1.nseconds() < t2.nseconds()) return true;
117 inline bool operator==(const time_val &t1, const time_val &t2) noexcept
119 return (t1.seconds() == t2.seconds() && t1.nseconds() == t2.nseconds());
122 inline bool operator<=(const time_val &t1, const time_val &t2) noexcept
124 if (t1.seconds() < t2.seconds()) return true;
125 if (t1.seconds() == t2.seconds() && t1.nseconds() <= t2.nseconds()) return true;
129 inline bool operator!=(const time_val &t1, const time_val &t2) noexcept { return !(t1 == t2); }
130 inline bool operator>(const time_val &t1, const time_val &t2) noexcept { return t2 < t1; }
131 inline bool operator>=(const time_val &t1, const time_val &t2) noexcept { return t2 <= t1; }
133 static inline int divide_timespec(const struct timespec &num, const struct timespec &den, struct timespec &rem) noexcept;
135 inline int operator/(const time_val &t1, const time_val &t2) noexcept
137 struct timespec remainder;
138 return divide_timespec(t1.get_timespec(), t2.get_timespec(), remainder);
141 inline time_val & operator<<=(time_val &t, int n) noexcept
143 for (int i = 0; i < n; i++) {
146 if (t.nseconds() >= 1000000000) {
147 t.nseconds() -= 1000000000;
154 inline time_val operator<<(time_val &t, int n) noexcept
161 inline time_val & operator>>=(time_val &t, int n) noexcept
163 for (int i = 0; i < n; i++) {
164 bool low = t.seconds() & 1;
166 t.nseconds() += low ? 500000000ULL : 0;
172 inline time_val operator>>(time_val &t, int n) noexcept
179 // Data corresponding to a single timer
183 time_val interval_time; // interval (if 0, one-off timer)
184 int expiry_count; // number of times expired
185 bool enabled; // whether timer reports events
188 timer_data(void *udata = nullptr) noexcept : interval_time(0,0), expiry_count(0), enabled(true), userdata(udata)
194 class compare_timespec
197 bool operator()(const struct timespec &a, const struct timespec &b) noexcept
199 if (a.tv_sec < b.tv_sec) {
203 if (a.tv_sec == b.tv_sec) {
204 return a.tv_nsec < b.tv_nsec;
211 using timer_queue_t = dary_heap<timer_data, time_val, compare_timespec>;
212 using timer_handle_t = timer_queue_t::handle_t;
214 static inline void init_timer_handle(timer_handle_t &hnd) noexcept
216 timer_queue_t::init_handle(hnd);
219 static inline int divide_timespec(const struct timespec &num, const struct timespec &den, struct timespec &rem) noexcept
221 if (num.tv_sec < den.tv_sec) {
226 if (num.tv_sec == den.tv_sec) {
227 if (num.tv_nsec < den.tv_nsec) {
231 if (num.tv_sec == 0) {
233 rem.tv_nsec = num.tv_nsec % den.tv_nsec;
234 return num.tv_nsec / den.tv_nsec;
236 // num.tv_sec == den.tv_sec and both are >= 1.
237 // The result can only be 1:
239 rem.tv_nsec = num.tv_nsec - den.tv_nsec;
243 // At this point, num.tv_sec >= 1.
245 time_val n = { num.tv_sec, num.tv_nsec };
246 time_val d = { den.tv_sec, den.tv_nsec };
249 // starting with numerator, subtract 1*denominator
252 // Check now for common case: one timer expiry with no overrun
259 int rval = 1; // we have subtracted 1*D already
261 // shift denominator until it is greater than / roughly equal to numerator:
262 while (d.seconds() < r.seconds()) {
281 template <typename Base> class timer_base : public Base
284 timer_queue_t timer_queue;
286 #if defined(CLOCK_MONOTONIC)
287 timer_queue_t mono_timer_queue;
290 inline timer_queue_t &queue_for_clock(clock_type clock)
292 if (clock == clock_type::MONOTONIC) {
293 return mono_timer_queue;
300 inline bool timer_queues_empty()
302 return timer_queue.empty() && mono_timer_queue.empty();
306 inline timer_queue_t &queue_for_clock(clock_type clock)
311 inline bool timer_queues_empty()
313 return timer_queue.empty();
317 // For the specified timer queue, issue expirations for all timers set to expire on or before the given
318 // time (curtime). The timer queue must not be empty.
319 void process_timer_queue(timer_queue_t &queue, const struct timespec &curtime) noexcept
321 // Peek timer queue; calculate difference between current time and timeout
322 const time_val * timeout = &queue.get_root_priority();
323 time_val curtime_tv = curtime;
324 while (*timeout <= curtime_tv) {
325 auto & thandle = queue.get_root();
326 timer_data &data = queue.node_data(thandle);
327 time_val &interval = data.interval_time;
330 if (interval.seconds() == 0 && interval.nseconds() == 0) {
331 // Non periodic timer
333 data.enabled = false;
334 int expiry_count = data.expiry_count;
335 data.expiry_count = 0;
336 Base::receive_timer_expiry(thandle, data.userdata, expiry_count);
343 // First calculate the overrun in time:
344 time_val overrun = time_val(curtime) - time_val(*timeout);
346 // Now we have to divide the time overrun by the period to find the
347 // interval overrun. This requires a division of a value not representable
350 data.expiry_count += divide_timespec(overrun, interval, rem);
352 // new time is current time + interval - remainder:
353 time_val newtime = curtime + interval - rem;
355 queue.insert(thandle, newtime);
357 data.enabled = false;
358 int expiry_count = data.expiry_count;
359 data.expiry_count = 0;
360 Base::receive_timer_expiry(thandle, data.userdata, expiry_count);
364 // repeat until all expired timeouts processed
365 timeout = &queue.get_root_priority();
371 void get_time(time_val &tv, clock_type clock, bool force_update) noexcept
373 get_time(tv.get_timespec(), clock, force_update);
376 #ifdef CLOCK_MONOTONIC
377 void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
379 clockid_t posix_clock_id = (clock == clock_type::MONOTONIC) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
380 clock_gettime(posix_clock_id, &ts);
383 // If CLOCK_MONOTONIC is not defined, assume we only have gettimeofday():
384 void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
386 struct timeval curtime_tv;
387 gettimeofday(&curtime_tv, nullptr);
388 ts.tv_sec = curtime_tv.tv_sec;
389 ts.tv_nsec = curtime_tv.tv_usec * 1000;
393 void add_timer_nolock(timer_handle_t &h, void *userdata, clock_type clock = clock_type::MONOTONIC)
395 this->queue_for_clock(clock).allocate(h, userdata);
398 void remove_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
400 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
401 remove_timer_nolock(timer_id, clock);
404 void remove_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
406 auto &timer_queue = this->queue_for_clock(clock);
407 if (timer_queue.is_queued(timer_id)) {
408 timer_queue.remove(timer_id);
410 timer_queue.deallocate(timer_id);
413 // Enables or disabling report of timeouts (does not stop timer)
414 void enable_timer(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
416 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
417 enable_timer_nolock(timer_id, enable, clock);
420 void enable_timer_nolock(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
422 auto &timer_queue = this->queue_for_clock(clock);
423 auto &node_data = timer_queue.node_data(timer_id);
424 auto expiry_count = node_data.expiry_count;
425 if (expiry_count != 0 && enable) {
426 node_data.expiry_count = 0;
427 Base::receive_timer_expiry(timer_id, node_data.userdata, expiry_count);
430 timer_queue.node_data(timer_id).enabled = enable;
437 #endif /* DASYNQ_TIMERBASE_H_INCLUDED */