4 #include <sys/timerfd.h>
7 #include "dasynq-timerbase.h"
11 // Timer implementation based on Linux's "timerfd".
13 // We could use one timerfd per timer, but then we need to differentiate timer
14 // descriptors from regular file descriptors when events are reported by the loop
15 // mechanism so that we can correctly report a timer event or fd event.
17 // With a file descriptor or signal, we can use the item itself as the identifier for
18 // adding/removing watches. For timers, it's more complicated. When we add a timer,
19 // we are given a handle; we need to use this to modify the watch. We delegate the
20 // process of allocating a handle to a priority heap implementation (BinaryHeap).
22 template <class Base> class timer_fd_events : public timer_base<Base>
26 int systemtime_fd = -1;
28 // Set the timerfd timeout to match the first timer in the queue (disable the timerfd
29 // if there are no active timers).
30 static void set_timer_from_queue(int fd, timer_queue_t &queue) noexcept
32 struct itimerspec newtime;
34 newtime.it_value = {0, 0};
35 newtime.it_interval = {0, 0};
38 newtime.it_value = queue.get_root_priority();
39 newtime.it_interval = {0, 0};
41 timerfd_settime(fd, TFD_TIMER_ABSTIME, &newtime, nullptr);
44 void process_timer(clock_type clock, int fd) noexcept
46 timer_queue_t &queue = this->queue_for_clock(clock);
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 set_timer(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);
93 class traits_t : public Base::traits_t
95 constexpr static bool full_timer_support = true;
99 std::tuple<int, typename traits_t::fd_s>
100 receive_fd_event(T &loop_mech, typename traits_t::fd_r fd_r_a, void * userdata, int flags)
102 if (userdata == &timerfd_fd) {
103 process_timer(clock_type::MONOTONIC, timerfd_fd);
104 return std::make_tuple(IN_EVENTS, typename traits_t::fd_s(timerfd_fd));
106 else if (userdata == &systemtime_fd) {
107 process_timer(clock_type::SYSTEM, systemtime_fd);
108 if (Base::traits_t::supports_non_oneshot_fd) {
109 return std::make_tuple(0, typename traits_t::fd_s(systemtime_fd));
111 return std::make_tuple(IN_EVENTS, typename traits_t::fd_s(systemtime_fd));
114 return Base::receive_fd_event(loop_mech, fd_r_a, 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->add_fd_watch(timerfd_fd, &timerfd_fd, IN_EVENTS);
132 loop_mech->add_fd_watch(systemtime_fd, &systemtime_fd, IN_EVENTS);
133 Base::init(loop_mech);
137 close(systemtime_fd);
142 void stop_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
144 std::lock_guard<decltype(Base::lock)> guard(Base::lock);
145 stop_timer_nolock(timer_id, clock);
148 void stop_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
150 timer_queue_t &queue = this->queue_for_clock(clock);
151 int fd = (clock == clock_type::MONOTONIC) ? timerfd_fd : systemtime_fd;
152 if (queue.is_queued(timer_id)) {
153 bool was_first = (&queue.get_root()) == &timer_id;
154 queue.remove(timer_id);
156 set_timer_from_queue(fd, queue);
161 // starts (if not started) a timer to timeout at the given time. Resets the expiry count to 0.
162 // enable: specifies whether to enable reporting of timeouts/intervals
163 void set_timer(timer_handle_t & timer_id, const time_val &timeouttv, const time_val &intervaltv,
164 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
166 timespec timeout = timeouttv;
167 timespec interval = intervaltv;
168 timer_queue_t &queue = this->queue_for_clock(clock);
171 case clock_type::SYSTEM:
172 set_timer(timer_id, timeout, interval, queue, systemtime_fd, enable);
174 case clock_type::MONOTONIC:
175 set_timer(timer_id, timeout, interval, queue, timerfd_fd, enable);
182 // Set timer relative to current time:
183 void set_timer_rel(timer_handle_t & timer_id, const time_val &timeout, const time_val &interval,
184 bool enable, clock_type clock = clock_type::MONOTONIC) noexcept
187 this->get_time(alarmtime, clock, false);
188 alarmtime += timeout;
190 set_timer(timer_id, alarmtime, interval, enable, clock);
196 close(systemtime_fd);