7 #include "dasynq-timerbase.h"
11 // Timer implementation based on the (basically obsolete) POSIX itimer interface.
13 // With this timer implementation, we only use one clock, and allow no distinction between the
14 // monotonic and system time.
16 template <class Base> class itimer_events : public timer_base<Base>
20 // Set the alarm timeout to match the first timer in the queue (disable the alarm if there are no
22 void set_timer_from_queue()
25 struct itimerval newalarm;
27 bool interval_set = false;
28 time_val interval_tv = {0, 0};
30 auto &timer_queue = this->queue_for_clock(clock_type::SYSTEM);
31 if (! timer_queue.empty()) {
32 newtime = timer_queue.get_root_priority();
35 timer_base<Base>::get_time(curtimev, clock_type::SYSTEM, true);
37 // interval before next timeout:
38 if (curtimev < newtime) {
39 interval_tv = newtime - curtimev;
45 #ifdef CLOCK_MONOTONIC
46 auto &mono_timer_queue = this->queue_for_clock(clock_type::MONOTONIC);
48 if (! mono_timer_queue.empty()) {
50 // If we have a separate monotonic clock, we get the interval for the expiry of the next monotonic
51 // timer and use the lesser of the system interval and monotonic interval:
52 time_val mono_newtime = mono_timer_queue.get_root_priority();
54 time_val curtimev_mono;
55 timer_base<Base>::get_time(curtimev_mono, clock_type::MONOTONIC, true);
57 time_val interval_mono = {0, 0};
58 if (curtimev_mono < mono_newtime) {
59 interval_mono = mono_newtime - curtimev_mono;
62 if (! interval_set || interval_mono < interval_tv) {
63 interval_tv = interval_mono;
70 newalarm.it_value.tv_sec = interval_tv.seconds();
71 newalarm.it_value.tv_usec = interval_tv.nseconds() / 1000;
72 newalarm.it_interval.tv_sec = 0;
73 newalarm.it_interval.tv_usec = 0;
75 if (interval_set && newalarm.it_value.tv_sec == 0 && newalarm.it_value.tv_usec == 0) {
76 // We passed the timeout: set alarm to expire immediately (we must use {0,1} as
77 // {0,0} disables the timer).
78 // TODO: it would be better if we just processed the appropriate timers here,
79 // but that is complicated to get right especially if the event loop
81 newalarm.it_value.tv_sec = 0;
82 newalarm.it_value.tv_usec = 1;
85 setitimer(ITIMER_REAL, &newalarm, nullptr);
90 using sigdata_t = typename Base::sigdata_t;
93 bool receive_signal(T & loop_mech, sigdata_t &siginfo, void *userdata)
95 if (siginfo.get_signo() == SIGALRM) {
96 auto &timer_queue = this->queue_for_clock(clock_type::SYSTEM);
97 if (! timer_queue.empty()) {
98 struct timespec curtime;
99 timer_base<Base>::get_time(curtime, clock_type::SYSTEM, true);
100 timer_base<Base>::process_timer_queue(timer_queue, curtime);
103 #ifdef CLOCK_MONOTONIC
104 auto &mono_timer_queue = this->queue_for_clock(clock_type::MONOTONIC);
105 if (! mono_timer_queue.empty()) {
106 struct timespec curtime_mono;
107 timer_base<Base>::get_time(curtime_mono, clock_type::MONOTONIC, true);
108 timer_base<Base>::process_timer_queue(mono_timer_queue, curtime_mono);
112 // arm alarm with timeout from head of queue
113 set_timer_from_queue();
114 return false; // don't disable signal watch
117 return Base::receive_signal(loop_mech, siginfo, userdata);
123 class traits_t : public Base::traits_t
125 constexpr static bool full_timer_support = false;
128 template <typename T> void init(T *loop_mech)
131 this->sigmaskf(SIG_UNBLOCK, nullptr, &sigmask);
132 sigaddset(&sigmask, SIGALRM);
133 this->sigmaskf(SIG_SETMASK, &sigmask, nullptr);
134 loop_mech->add_signal_watch(SIGALRM, nullptr);
135 Base::init(loop_mech);
138 // starts (if not started) a timer to timeout at the given time. Resets the expiry count to 0.
139 // enable: specifies whether to enable reporting of timeouts/intervals
140 void set_timer(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
141 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
143 auto &timer_queue = this->queue_for_clock(clock);
144 timespec timeout = timeouttv;
145 timespec interval = intervaltv;
147 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
149 auto &ts = timer_queue.node_data(timer_id);
150 ts.interval_time = interval;
154 if (timer_queue.is_queued(timer_id)) {
155 // Already queued; alter timeout
156 if (timer_queue.set_priority(timer_id, timeout)) {
157 set_timer_from_queue();
161 if (timer_queue.insert(timer_id, timeout)) {
162 set_timer_from_queue();
167 // Set timer relative to current time:
168 void set_timer_rel(timer_handle_t &timer_id, const time_val &timeouttv, const time_val &intervaltv,
169 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
171 timespec timeout = timeouttv;
172 timespec interval = intervaltv;
174 struct timespec curtime;
175 timer_base<Base>::get_time(curtime, clock, false);
176 curtime.tv_sec += timeout.tv_sec;
177 curtime.tv_nsec += timeout.tv_nsec;
178 if (curtime.tv_nsec > 1000000000) {
179 curtime.tv_nsec -= 1000000000;
182 set_timer(timer_id, curtime, interval, enable, clock);
185 void stop_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
187 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
188 stop_timer_nolock(timer_id, clock);
191 void stop_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
193 auto &timer_queue = this->queue_for_clock(clock);
194 if (timer_queue.is_queued(timer_id)) {
195 bool was_first = (&timer_queue.get_root()) == &timer_id;
196 timer_queue.remove(timer_id);
198 set_timer_from_queue();