Update bundled Dasynq to 1.1.4.
[oweals/dinit.git] / src / dasynq / dasynq-itimer.h
1 #include <vector>
2 #include <utility>
3
4 #include <signal.h>
5 #include <sys/time.h>
6 #include <time.h>
7
8 #include "dasynq-timerbase.h"
9
10 namespace dasynq {
11
12 // Timer implementation based on the (basically obsolete) POSIX itimer interface.
13
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).
18
19 template <class Base, bool provide_mono_timer = true>
20 class itimer_events : public timer_base<Base>
21 {
22     private:
23     
24     // Set the alarm timeout to match the first timer in the queue (disable the alarm if there are no
25     // active timers).
26     void set_timer_from_queue()
27     {
28         time_val newtime;
29         struct itimerval newalarm;
30
31         bool interval_set = false;
32         time_val interval_tv = {0, 0};
33
34         auto &timer_queue = this->queue_for_clock(clock_type::SYSTEM);
35         if (! timer_queue.empty()) {
36             newtime = timer_queue.get_root_priority();
37
38             time_val curtimev;
39             timer_base<Base>::get_time(curtimev, clock_type::SYSTEM, true);
40
41             // interval before next timeout:
42             if (curtimev < newtime) {
43                 interval_tv = newtime - curtimev;
44             }
45
46             interval_set = true;
47         }
48
49 #ifdef CLOCK_MONOTONIC
50         auto &mono_timer_queue = this->queue_for_clock(clock_type::MONOTONIC);
51
52         if (! mono_timer_queue.empty()) {
53
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();
57
58             time_val curtimev_mono;
59             timer_base<Base>::get_time(curtimev_mono, clock_type::MONOTONIC, true);
60
61             time_val interval_mono = {0, 0};
62             if (curtimev_mono < mono_newtime) {
63                 interval_mono = mono_newtime - curtimev_mono;
64             }
65
66             if (! interval_set || interval_mono < interval_tv) {
67                 interval_tv = interval_mono;
68             }
69
70             interval_set = true;
71         }
72 #endif
73
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;
78
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
84             //       is multi-threaded.
85             newalarm.it_value.tv_sec = 0;
86             newalarm.it_value.tv_usec = 1;
87         }
88
89         setitimer(ITIMER_REAL, &newalarm, nullptr);
90     }
91     
92     protected:
93     
94     using sigdata_t = typename Base::sigdata_t;
95
96     template <typename T>
97     bool receive_signal(T & loop_mech, sigdata_t &siginfo, void *userdata)
98     {
99         if (siginfo.get_signo() == SIGALRM) {
100             process_timers();
101             return false; // don't disable signal watch
102         }
103         else {
104             return Base::receive_signal(loop_mech, siginfo, userdata);
105         }
106     }
107
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:
111
112     inline void process_monotonic_timers(bool &do_wait, timeval &tv, timeval *&wait_tv)
113     {
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);
117
118         if (! do_wait) {
119             timer_base<Base>::process_timers(clock_type::SYSTEM, do_wait, tv, wait_tv);
120         }
121         else {
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)) {
126                 tv = mono_tv;
127             }
128         }
129     }
130
131     inline void process_monotonic_timers(bool &do_wait, timespec &ts, timespec *&wait_ts)
132     {
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);
136
137         if (! do_wait) {
138             timer_base<Base>::process_timers(clock_type::SYSTEM, do_wait, ts, wait_ts);
139         }
140         else {
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));
144         }
145     }
146
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()
150     {
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);
156         }
157
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);
163         }
164     }
165
166 #endif
167
168     void process_timers()
169     {
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);
175         }
176         
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);
184             }
185         }
186 #endif
187
188         if (provide_mono_timer) {
189             // arm alarm with timeout from head of queue
190             set_timer_from_queue();
191         }
192     }
193
194     public:
195
196     class traits_t : public Base::traits_t
197     {
198         constexpr static bool full_timer_support = false;
199     };
200
201     template <typename T> void init(T *loop_mech)
202     {
203         if (provide_mono_timer) {
204             sigset_t sigmask;
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);
209         }
210         Base::init(loop_mech);
211     }
212     
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
217     {
218         auto &timer_queue = this->queue_for_clock(clock);
219         timespec timeout = timeouttv;
220         timespec interval = intervaltv;
221
222         std::lock_guard<decltype(Base::lock)> guard(Base::lock);
223
224         auto &ts = timer_queue.node_data(timer_id);
225         ts.interval_time = interval;
226         ts.expiry_count = 0;
227         ts.enabled = enable;
228
229         bool do_set_timer;
230         if (timer_queue.is_queued(timer_id)) {
231             // Already queued; alter timeout
232             do_set_timer = timer_queue.set_priority(timer_id, timeout);
233         }
234         else {
235             do_set_timer = timer_queue.insert(timer_id, timeout);
236         }
237
238         if (do_set_timer) {
239             if (provide_mono_timer) {
240                 set_timer_from_queue();
241             }
242             else {
243                 this->interrupt_wait();
244             }
245         }
246     }
247
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
251     {
252         timespec timeout = timeouttv;
253         timespec interval = intervaltv;
254
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;
261             curtime.tv_sec++;
262         }
263         set_timer(timer_id, curtime, interval, enable, clock);
264     }
265     
266     void stop_timer(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
267     {
268         std::lock_guard<decltype(Base::lock)> guard(Base::lock);
269         stop_timer_nolock(timer_id, clock);
270     }
271
272     void stop_timer_nolock(timer_handle_t &timer_id, clock_type clock = clock_type::MONOTONIC) noexcept
273     {
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);
278             if (was_first) {
279                 set_timer_from_queue();
280             }
281         }
282     }
283 };
284
285 }