4 #include <sys/timerfd.h>
7 #include "dasynq-timerbase.h"
11 // We could use one timerfd per timer, but then we need to differentiate timer
12 // descriptors from regular file descriptors when events are reported by the loop
13 // mechanism so that we can correctly report a timer event or fd event.
15 // With a file descriptor or signal, we can use the item itself as the identifier for
16 // adding/removing watches. For timers, it's more complicated. When we add a timer,
17 // we are given a handle; we need to use this to modify the watch. We delegate the
18 // process of allocating a handle to a priority heap implementation (BinaryHeap).
20 template <class Base> class TimerFdEvents : public timer_base<Base>
24 int systemtime_fd = -1;
26 timer_queue_t timer_queue;
27 timer_queue_t wallclock_queue;
29 // Set the timerfd timeout to match the first timer in the queue (disable the timerfd
30 // if there are no active timers).
31 static void set_timer_from_queue(int fd, timer_queue_t &queue) noexcept
33 struct itimerspec newtime;
35 newtime.it_value = {0, 0};
36 newtime.it_interval = {0, 0};
39 newtime.it_value = queue.get_root_priority();
40 newtime.it_interval = {0, 0};
42 timerfd_settime(fd, TFD_TIMER_ABSTIME, &newtime, nullptr);
45 void process_timer(clock_type clock, int fd, timer_queue_t &queue) noexcept
47 struct timespec curtime;
49 case clock_type::SYSTEM:
50 clock_gettime(CLOCK_REALTIME, &curtime);
52 case clock_type::MONOTONIC:
53 clock_gettime(CLOCK_MONOTONIC, &curtime);
59 timer_base<Base>::process_timer_queue(queue, curtime);
61 // arm timerfd with timeout from head of queue
62 set_timer_from_queue(fd, queue);
65 void setTimer(timer_handle_t & timer_id, const time_val &timeouttv, const time_val &intervaltv,
66 timer_queue_t &queue, int fd, bool enable) noexcept
68 timespec timeout = timeouttv;
69 timespec interval = intervaltv;
71 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
73 auto &ts = queue.node_data(timer_id);
74 ts.interval_time = interval;
78 if (queue.is_queued(timer_id)) {
79 // Already queued; alter timeout
80 if (queue.set_priority(timer_id, timeout)) {
81 set_timer_from_queue(fd, queue);
85 if (queue.insert(timer_id, timeout)) {
86 set_timer_from_queue(fd, queue);
91 timer_queue_t & get_queue(clock_type clock)
94 case clock_type::SYSTEM:
95 return wallclock_queue;
96 case clock_type::MONOTONIC:
104 template <typename T>
105 void receiveFdEvent(T &loop_mech, typename Base::FD_r fd_r, void * userdata, int flags)
107 if (userdata == &timerfd_fd) {
108 process_timer(clock_type::MONOTONIC, timerfd_fd, timer_queue);
110 else if (userdata == &systemtime_fd) {
111 process_timer(clock_type::SYSTEM, systemtime_fd, wallclock_queue);
114 Base::receiveFdEvent(loop_mech, fd_r, userdata, flags);
118 template <typename T> void init(T *loop_mech)
120 timerfd_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK);
121 if (timerfd_fd == -1) {
122 throw std::system_error(errno, std::system_category());
124 systemtime_fd = timerfd_create(CLOCK_REALTIME, TFD_CLOEXEC | TFD_NONBLOCK);
125 if (systemtime_fd == -1) {
127 throw std::system_error(errno, std::system_category());
131 loop_mech->addFdWatch(timerfd_fd, &timerfd_fd, IN_EVENTS);
132 loop_mech->addFdWatch(systemtime_fd, &systemtime_fd, IN_EVENTS);
133 Base::init(loop_mech);
137 close(systemtime_fd);
142 // Add timer, store into given handle
143 void addTimer(timer_handle_t &h, void *userdata, clock_type clock = clock_type::MONOTONIC)
145 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
146 timer_queue_t & queue = get_queue(clock);
147 queue.allocate(h, userdata);
150 void removeTimer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
152 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
153 removeTimer_nolock(timer_id, clock);
156 void removeTimer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
158 timer_queue_t & queue = get_queue(clock);
159 if (queue.is_queued(timer_id)) {
160 queue.remove(timer_id);
162 queue.deallocate(timer_id);
165 void stop_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
167 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
168 stop_timer_nolock(timer_id, clock);
171 void stop_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
173 timer_queue_t & queue = get_queue(clock);
174 if (queue.is_queued(timer_id)) {
175 queue.remove(timer_id);
179 // starts (if not started) a timer to timeout at the given time. Resets the expiry count to 0.
180 // enable: specifies whether to enable reporting of timeouts/intervals
181 void setTimer(timer_handle_t & timer_id, const time_val &timeouttv, const time_val &intervaltv,
182 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
184 timespec timeout = timeouttv;
185 timespec interval = intervaltv;
188 case clock_type::SYSTEM:
189 setTimer(timer_id, timeout, interval, wallclock_queue, systemtime_fd, enable);
191 case clock_type::MONOTONIC:
192 setTimer(timer_id, timeout, interval, timer_queue, timerfd_fd, enable);
199 // Set timer relative to current time:
200 void setTimerRel(timer_handle_t & timer_id, const time_val &timeouttv, const time_val &intervaltv,
201 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
203 timespec timeout = timeouttv;
204 timespec interval = intervaltv;
208 case clock_type::SYSTEM:
209 sclock = CLOCK_REALTIME;
211 case clock_type::MONOTONIC:
212 sclock = CLOCK_MONOTONIC;
218 // TODO consider caching current time somehow; need to decide then when to update cached value.
219 struct timespec curtime;
220 clock_gettime(sclock, &curtime);
221 curtime.tv_sec += timeout.tv_sec;
222 curtime.tv_nsec += timeout.tv_nsec;
223 if (curtime.tv_nsec > 1000000000) {
224 curtime.tv_nsec -= 1000000000;
228 setTimer(timer_id, curtime, interval, enable, clock);
231 // Enables or disabling report of timeouts (does not stop timer)
232 void enableTimer(timer_handle_t & timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
234 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
235 enableTimer_nolock(timer_id, enable, clock);
238 void enableTimer_nolock(timer_handle_t & timer_id, bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
240 timer_queue_t & queue = get_queue(clock);
242 auto &node_data = queue.node_data(timer_id);
243 auto expiry_count = node_data.expiry_count;
244 if (expiry_count != 0) {
245 node_data.expiry_count = 0;
246 Base::receiveTimerExpiry(timer_id, node_data.userdata, expiry_count);
249 queue.node_data(timer_id).enabled = enable;
253 void get_time(time_val &tv, clock_type clock, bool force_update) noexcept
256 get_time(ts, clock, force_update);
260 void get_time(timespec &ts, clock_type clock, bool force_update) noexcept
262 int posix_clock_id = (clock == clock_type::MONOTONIC) ? CLOCK_MONOTONIC : CLOCK_REALTIME;
263 clock_gettime(posix_clock_id, &ts);
269 close(systemtime_fd);