8 #include "dasynq-timerbase.h"
12 // Timer implementation based on the (basically obsolete) POSIX itimer interface.
14 // With this timer implementation, we only have one real clock (the monotonic clock) that we can
15 // run a timer against. However, if the system has both clocks, we still maintain two separate queues.
16 // If provide_mono_timer is false we actually provide no system timer and rely on the event mechanism
17 // which extends this class to measure time and run timeouts (via process_monotonic_timers functions).
19 template <class Base, bool provide_mono_timer = true>
20 class itimer_events : public timer_base<Base>
24 // Set the alarm timeout to match the first timer in the queue (disable the alarm if there are no
26 void set_timer_from_queue()
29 struct itimerval newalarm;
31 bool interval_set = false;
32 time_val interval_tv = {0, 0};
34 auto &timer_queue = this->queue_for_clock(clock_type::SYSTEM);
35 if (! timer_queue.empty()) {
36 newtime = timer_queue.get_root_priority();
39 timer_base<Base>::get_time(curtimev, clock_type::SYSTEM, true);
41 // interval before next timeout:
42 if (curtimev < newtime) {
43 interval_tv = newtime - curtimev;
49 #ifdef CLOCK_MONOTONIC
50 auto &mono_timer_queue = this->queue_for_clock(clock_type::MONOTONIC);
52 if (! mono_timer_queue.empty()) {
54 // If we have a separate monotonic clock, we get the interval for the expiry of the next monotonic
55 // timer and use the lesser of the system interval and monotonic interval:
56 time_val mono_newtime = mono_timer_queue.get_root_priority();
58 time_val curtimev_mono;
59 timer_base<Base>::get_time(curtimev_mono, clock_type::MONOTONIC, true);
61 time_val interval_mono = {0, 0};
62 if (curtimev_mono < mono_newtime) {
63 interval_mono = mono_newtime - curtimev_mono;
66 if (! interval_set || interval_mono < interval_tv) {
67 interval_tv = interval_mono;
74 newalarm.it_value.tv_sec = interval_tv.seconds();
75 newalarm.it_value.tv_usec = interval_tv.nseconds() / 1000;
76 newalarm.it_interval.tv_sec = 0;
77 newalarm.it_interval.tv_usec = 0;
79 if (interval_set && newalarm.it_value.tv_sec == 0 && newalarm.it_value.tv_usec == 0) {
80 // We passed the timeout: set alarm to expire immediately (we must use {0,1} as
81 // {0,0} disables the timer).
82 // TODO: it would be better if we just processed the appropriate timers here,
83 // but that is complicated to get right especially if the event loop
85 newalarm.it_value.tv_sec = 0;
86 newalarm.it_value.tv_usec = 1;
89 setitimer(ITIMER_REAL, &newalarm, nullptr);
94 using sigdata_t = typename Base::sigdata_t;
97 bool receive_signal(T & loop_mech, sigdata_t &siginfo, void *userdata)
99 if (siginfo.get_signo() == SIGALRM) {
101 return false; // don't disable signal watch
104 return Base::receive_signal(loop_mech, siginfo, userdata);
108 #ifdef CLOCK_MONOTONIC
109 // We need to override the monotonic timer processing functions from timer_base to check both
110 // timer queues, since we aren't actually providing a separate timer for the system clock:
112 inline void process_monotonic_timers(bool &do_wait, timeval &tv, timeval *&wait_tv)
114 // We need to process both timer queues and set tv/wait_tv according to which has the
115 // timer that will expire soonest:
116 timer_base<Base>::process_timers(clock_type::MONOTONIC, do_wait, tv, wait_tv);
119 timer_base<Base>::process_timers(clock_type::SYSTEM, do_wait, tv, wait_tv);
122 timeval mono_tv = tv;
123 timer_base<Base>::process_timers(clock_type::SYSTEM, do_wait, tv, wait_tv);
124 if (mono_tv.tv_sec < tv.tv_sec ||
125 (mono_tv.tv_sec == tv.tv_sec && mono_tv.tv_usec < tv.tv_usec)) {
131 inline void process_monotonic_timers(bool &do_wait, timespec &ts, timespec *&wait_ts)
133 // We need to process both timer queues and set tv/wait_tv according to which has the
134 // timer that will expire soonest:
135 timer_base<Base>::process_timers(clock_type::MONOTONIC, do_wait, ts, wait_ts);
138 timer_base<Base>::process_timers(clock_type::SYSTEM, do_wait, ts, wait_ts);
141 timespec mono_ts = ts;
142 timer_base<Base>::process_timers(clock_type::SYSTEM, do_wait, ts, wait_ts);
143 ts = std::min(time_val(ts), time_val(mono_ts));
147 // Process monotonic timers based on the current clock time. However, we shadow the
148 // function and provide an implementation which also processes the system clock timer queue.
149 inline void process_monotonic_timers()
151 auto &mono_timer_queue = this->queue_for_clock(clock_type::MONOTONIC);
152 if (! mono_timer_queue.empty()) {
153 struct timespec curtime_mono;
154 timer_base<Base>::get_time(curtime_mono, clock_type::MONOTONIC, true);
155 timer_base<Base>::process_timer_queue(mono_timer_queue, curtime_mono);
158 auto &sys_timer_queue = this->queue_for_clock(clock_type::SYSTEM);
159 if (! sys_timer_queue.empty()) {
160 struct timespec curtime_sys;
161 timer_base<Base>::get_time(curtime_sys, clock_type::SYSTEM, true);
162 timer_base<Base>::process_timer_queue(sys_timer_queue, curtime_sys);
168 void process_timers()
170 auto &timer_queue = this->queue_for_clock(clock_type::SYSTEM);
171 if (! timer_queue.empty()) {
172 struct timespec curtime;
173 timer_base<Base>::get_time(curtime, clock_type::SYSTEM, true);
174 timer_base<Base>::process_timer_queue(timer_queue, curtime);
177 #ifdef CLOCK_MONOTONIC
178 if (provide_mono_timer) {
179 auto &mono_timer_queue = this->queue_for_clock(clock_type::MONOTONIC);
180 if (! mono_timer_queue.empty()) {
181 struct timespec curtime_mono;
182 timer_base<Base>::get_time(curtime_mono, clock_type::MONOTONIC, true);
183 timer_base<Base>::process_timer_queue(mono_timer_queue, curtime_mono);
188 if (provide_mono_timer) {
189 // arm alarm with timeout from head of queue
190 set_timer_from_queue();
196 class traits_t : public Base::traits_t
198 constexpr static bool full_timer_support = false;
201 template <typename T> void init(T *loop_mech)
203 if (provide_mono_timer) {
205 this->sigmaskf(SIG_UNBLOCK, nullptr, &sigmask);
206 sigaddset(&sigmask, SIGALRM);
207 this->sigmaskf(SIG_SETMASK, &sigmask, nullptr);
208 loop_mech->add_signal_watch(SIGALRM, this);
210 Base::init(loop_mech);
213 // starts (if not started) a timer to timeout at the given time. Resets the expiry count to 0.
214 // enable: specifies whether to enable reporting of timeouts/intervals
215 void set_timer(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
216 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
218 auto &timer_queue = this->queue_for_clock(clock);
219 timespec timeout = timeouttv;
220 timespec interval = intervaltv;
222 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
224 auto &ts = timer_queue.node_data(timer_id);
225 ts.interval_time = interval;
230 if (timer_queue.is_queued(timer_id)) {
231 // Already queued; alter timeout
232 do_set_timer = timer_queue.set_priority(timer_id, timeout);
235 do_set_timer = timer_queue.insert(timer_id, timeout);
239 if (provide_mono_timer) {
240 set_timer_from_queue();
243 this->interrupt_wait();
248 // Set timer relative to current time:
249 void set_timer_rel(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
250 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
252 timespec timeout = timeouttv;
253 timespec interval = intervaltv;
255 struct timespec curtime;
256 timer_base<Base>::get_time(curtime, clock, false);
257 curtime.tv_sec += timeout.tv_sec;
258 curtime.tv_nsec += timeout.tv_nsec;
259 if (curtime.tv_nsec > 1000000000) {
260 curtime.tv_nsec -= 1000000000;
263 set_timer(timer_id, curtime, interval, enable, clock);
266 void stop_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
268 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
269 stop_timer_nolock(timer_id, clock);
272 void stop_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
274 auto &timer_queue = this->queue_for_clock(clock);
275 if (timer_queue.is_queued(timer_id)) {
276 bool was_first = (&timer_queue.get_root()) == &timer_id;
277 timer_queue.remove(timer_id);
279 set_timer_from_queue();