7 #include "dasynq-timerbase.h"
11 // Timer implementation based on the (basically obselete) POSIX itimer interface.
13 template <class Base> class ITimerEvents : public timer_base<Base>
18 timer_queue_t timer_queue;
20 // Set the timerfd timeout to match the first timer in the queue (disable the timerfd
21 // if there are no active timers).
22 void set_timer_from_queue()
24 struct timespec newtime;
25 struct itimerval newalarm;
26 if (timer_queue.empty()) {
27 newalarm.it_value = {0, 0};
28 newalarm.it_interval = {0, 0};
29 setitimer(ITIMER_REAL, &newalarm, nullptr);
33 newtime = timer_queue.get_root_priority();
35 struct timespec curtime;
36 #if defined(__APPLE__)
37 struct timeval curtime_tv;
38 gettimeofday(&curtime_tv, nullptr);
39 curtime.tv_sec = curtime_tv.tv_sec;
40 curtime.tv_nsec = curtime_tv.tv_usec * 1000;
42 clock_gettime(CLOCK_MONOTONIC, &curtime);
44 newalarm.it_interval = {0, 0};
45 newalarm.it_value.tv_sec = newtime.tv_sec - curtime.tv_sec;
46 newalarm.it_value.tv_usec = (newtime.tv_nsec - curtime.tv_nsec) / 1000;
48 if (newalarm.it_value.tv_usec < 0) {
49 newalarm.it_value.tv_usec += 1000000;
50 newalarm.it_value.tv_sec--;
52 setitimer(ITIMER_REAL, &newalarm, nullptr);
57 using SigInfo = typename Base::SigInfo;
60 bool receiveSignal(T & loop_mech, SigInfo &siginfo, void *userdata)
62 if (siginfo.get_signo() == SIGALRM) {
63 struct timespec curtime;
64 #if defined(__APPLE__)
65 struct timeval curtime_tv;
66 gettimeofday(&curtime_tv, nullptr);
67 curtime.tv_sec = curtime_tv.tv_sec;
68 curtime.tv_nsec = curtime_tv.tv_usec * 1000;
70 clock_gettime(CLOCK_MONOTONIC, &curtime);
71 // should we use the REALTIME clock instead? I have no idea :/
74 timer_base<Base>::process_timer_queue(timer_queue, curtime);
76 // arm timerfd with timeout from head of queue
77 set_timer_from_queue();
78 // loop_mech.rearmSignalWatch_nolock(SIGALRM);
79 return false; // don't disable signal watch
82 return Base::receiveSignal(loop_mech, siginfo, userdata);
88 template <typename T> void init(T *loop_mech)
91 sigprocmask(SIG_UNBLOCK, nullptr, &sigmask);
92 sigaddset(&sigmask, SIGALRM);
93 sigprocmask(SIG_SETMASK, &sigmask, nullptr);
94 loop_mech->addSignalWatch(SIGALRM, nullptr);
95 Base::init(loop_mech);
98 void addTimer(timer_handle_t &h, void *userdata, clock_type clock = clock_type::MONOTONIC)
100 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
101 timer_queue.allocate(h, userdata);
104 void removeTimer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
106 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
107 removeTimer_nolock(timer_id, clock);
110 void removeTimer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
112 if (timer_queue.is_queued(timer_id)) {
113 timer_queue.remove(timer_id);
115 timer_queue.deallocate(timer_id);
118 // starts (if not started) a timer to timeout at the given time. Resets the expiry count to 0.
119 // enable: specifies whether to enable reporting of timeouts/intervals
120 void setTimer(timer_handle_t &timer_id, struct timespec &timeout, struct timespec &interval,
121 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
123 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
125 auto &ts = timer_queue.node_data(timer_id);
126 ts.interval_time = interval;
130 if (timer_queue.is_queued(timer_id)) {
131 // Already queued; alter timeout
132 if (timer_queue.set_priority(timer_id, timeout)) {
133 set_timer_from_queue();
137 if (timer_queue.insert(timer_id, timeout)) {
138 set_timer_from_queue();
143 // Set timer relative to current time:
144 void setTimerRel(timer_handle_t &timer_id, struct timespec &timeout, struct timespec &interval,
145 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
147 // TODO consider caching current time somehow; need to decide then when to update cached value.
148 struct timespec curtime;
149 #if defined(__APPLE__)
150 struct timeval curtime_tv;
151 gettimeofday(&curtime_tv, nullptr);
152 curtime.tv_sec = curtime_tv.tv_sec;
153 curtime.tv_nsec = curtime_tv.tv_usec * 1000;
155 clock_gettime(CLOCK_MONOTONIC, &curtime);
157 curtime.tv_sec += timeout.tv_sec;
158 curtime.tv_nsec += timeout.tv_nsec;
159 if (curtime.tv_nsec > 1000000000) {
160 curtime.tv_nsec -= 1000000000;
163 setTimer(timer_id, curtime, interval, enable);
166 // Enables or disabling report of timeouts (does not stop timer)
167 void enableTimer(timer_handle_t &timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
169 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
170 enableTimer_nolock(timer_id, enable, clock);
173 void enableTimer_nolock(timer_handle_t &timer_id, bool enable, clock_type = clock_type::MONOTONIC) noexcept
175 auto &node_data = timer_queue.node_data(timer_id);
176 auto expiry_count = node_data.expiry_count;
177 if (expiry_count != 0) {
178 node_data.expiry_count = 0;
179 Base::receiveTimerExpiry(timer_id, node_data.userdata, expiry_count);
182 timer_queue.node_data(timer_id).enabled = enable;
186 void stop_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
188 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
189 stop_timer_nolock(timer_id, clock);
192 void stop_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
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();