1 /* Watchdog and associated classes' implementation (non-inline functions).
2 Copyright (C) 2001-2010 Roberto Bagnara <bagnara@cs.unipr.it>
3 Copyright (C) 2010-2016 BUGSENG srl (http://bugseng.com)
4
5 This file is part of the Parma Polyhedra Library (PPL).
6
7 The PPL is free software; you can redistribute it and/or modify it
8 under the terms of the GNU General Public License as published by the
9 Free Software Foundation; either version 3 of the License, or (at your
10 option) any later version.
11
12 The PPL is distributed in the hope that it will be useful, but WITHOUT
13 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
15 for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1307, USA.
20
21 For the most up-to-date information see the Parma Polyhedra Library
22 site: http://bugseng.com/products/ppl/ . */
23
24 #include "ppl-config.h"
25 #include "Watchdog_defs.hh"
26
27 #if PPL_HAVE_DECL_SETITIMER && PPL_HAVE_DECL_SIGACTION
28
29 #include <csignal>
30 #include <iostream>
31 #include <stdexcept>
32 #include <cerrno>
33 #include <string>
34 #include <string.h>
35
36 #ifdef PPL_TIME_WITH_SYS_TIME
37 # include <sys/time.h>
38 # include <ctime>
39 #else
40 # ifdef PPL_HAVE_SYS_TIME_H
41 # include <sys/time.h>
42 # else
43 # include <ctime>
44 # endif
45 #endif
46
47 // Cygwin only supports ITIMER_REAL.
48 // Apparently GNU Hurd also only supports ITIMER_REAL
49 // (see http://www.cs.unipr.it/pipermail/ppl-devel/2010-March/016072.html).
50 // Profiling does not work on programs that use the ITIMER_PROF timer.
51 #if defined(__CYGWIN__) || defined(__gnu_hurd__) || defined(PPL_PROFILING)
52 #define THE_TIMER ITIMER_REAL
53 #define THE_SIGNAL SIGALRM
54 #else
55 #define THE_TIMER ITIMER_PROF
56 #define THE_SIGNAL SIGPROF
57 #endif
58
59 using std::cerr;
60 using std::endl;
61
62 namespace PPL = Parma_Polyhedra_Library;
63
64 // Pass this to getitimer().
65 itimerval PPL::Watchdog::current_timer_status;
66
67 // Pass this to setitimer().
68 itimerval PPL::Watchdog::signal_once;
69
70 // Last time value we set the timer to.
71 PPL::Implementation::Watchdog::Time PPL::Watchdog::last_time_requested;
72
73 // Records the time elapsed since last fresh start.
74 PPL::Implementation::Watchdog::Time PPL::Watchdog::time_so_far;
75
76 // The ordered queue of pending watchdog events.
77 PPL::Watchdog::WD_Pending_List PPL::Watchdog::pending;
78
79 // Whether the alarm clock is running.
80 volatile bool PPL::Watchdog::alarm_clock_running = false;
81
82 // Whether we are changing data which are also changed by the signal handler.
83 volatile bool PPL::Watchdog::in_critical_section = false;
84
85 namespace {
86
87 void
throw_syscall_error(const char * syscall_name)88 throw_syscall_error(const char* syscall_name) {
89 throw std::runtime_error(std::string(syscall_name) + ": " + strerror(errno));
90 }
91
92 void
my_getitimer(int which,struct itimerval * value)93 my_getitimer(int which, struct itimerval* value) {
94 if (getitimer(which, value) != 0) {
95 throw_syscall_error("getitimer");
96 }
97 }
98
99 void
my_setitimer(int which,const struct itimerval * value,struct itimerval * old_value)100 my_setitimer(int which,
101 const struct itimerval* value, struct itimerval* old_value) {
102 if (setitimer(which, value, old_value) != 0) {
103 throw_syscall_error("setitimer");
104 }
105 }
106
107 void
my_sigaction(int signum,const struct sigaction * act,struct sigaction * old_action)108 my_sigaction(int signum,
109 const struct sigaction* act, struct sigaction* old_action) {
110 if (sigaction(signum, act, old_action) != 0) {
111 throw_syscall_error("sigaction");
112 }
113 }
114
115 } // namespace
116
117 void
get_timer(Implementation::Watchdog::Time & time)118 PPL::Watchdog::get_timer(Implementation::Watchdog::Time& time) {
119 using namespace Implementation::Watchdog;
120 my_getitimer(THE_TIMER, ¤t_timer_status);
121 time = Time(current_timer_status.it_value.tv_sec,
122 current_timer_status.it_value.tv_usec);
123 }
124
125 void
set_timer(const Implementation::Watchdog::Time & time)126 PPL::Watchdog::set_timer(const Implementation::Watchdog::Time& time) {
127 if (time.seconds() == 0 && time.microseconds() == 0) {
128 throw std::runtime_error("PPL internal error");
129 }
130 last_time_requested = time;
131 signal_once.it_value.tv_sec = time.seconds();
132 signal_once.it_value.tv_usec = time.microseconds();
133 my_setitimer(THE_TIMER, &signal_once, 0);
134 }
135
136 void
stop_timer()137 PPL::Watchdog::stop_timer() {
138 signal_once.it_value.tv_sec = 0;
139 signal_once.it_value.tv_usec = 0;
140 my_setitimer(THE_TIMER, &signal_once, 0);
141 }
142
143 void
handle_timeout(int)144 PPL::Watchdog::handle_timeout(int) {
145 if (in_critical_section) {
146 reschedule();
147 }
148 else {
149 time_so_far += last_time_requested;
150 if (!pending.empty()) {
151 WD_Pending_List::iterator i = pending.begin();
152 do {
153 i->handler().act();
154 i->expired_flag() = true;
155 i = pending.erase(i);
156 } while (i != pending.end() && i->deadline() <= time_so_far);
157 if (pending.empty()) {
158 alarm_clock_running = false;
159 }
160 else {
161 set_timer((*pending.begin()).deadline() - time_so_far);
162 }
163 }
164 else {
165 alarm_clock_running = false;
166 }
167 }
168 }
169
170 void
PPL_handle_timeout(int signum)171 PPL::PPL_handle_timeout(int signum) {
172 PPL::Watchdog::handle_timeout(signum);
173 }
174
175 PPL::Watchdog::WD_Pending_List::iterator
new_watchdog_event(long csecs,const WD_Handler & handler,bool & expired_flag)176 PPL::Watchdog::new_watchdog_event(long csecs,
177 const WD_Handler& handler,
178 bool& expired_flag) {
179 using namespace Implementation::Watchdog;
180 assert(csecs > 0);
181 WD_Pending_List::iterator position;
182 const Time deadline(csecs);
183 if (!alarm_clock_running) {
184 position = pending.insert(deadline, handler, expired_flag);
185 time_so_far = Time(0);
186 set_timer(deadline);
187 alarm_clock_running = true;
188 }
189 else {
190 Time time_to_shoot;
191 get_timer(time_to_shoot);
192 Time elapsed_time(last_time_requested);
193 elapsed_time -= time_to_shoot;
194 Time current_time(time_so_far);
195 current_time += elapsed_time;
196 Time real_deadline(deadline);
197 real_deadline += current_time;
198 position = pending.insert(real_deadline, handler, expired_flag);
199 if (deadline < time_to_shoot) {
200 time_so_far = current_time;
201 set_timer(deadline);
202 }
203 }
204 return position;
205 }
206
207 void
remove_watchdog_event(WD_Pending_List::iterator position)208 PPL::Watchdog::remove_watchdog_event(WD_Pending_List::iterator position) {
209 using namespace Implementation::Watchdog;
210 assert(!pending.empty());
211 if (position == pending.begin()) {
212 WD_Pending_List::iterator next = position;
213 ++next;
214 if (next != pending.end()) {
215 const Time first_deadline(position->deadline());
216 Time next_deadline(next->deadline());
217 if (first_deadline != next_deadline) {
218 Time time_to_shoot;
219 get_timer(time_to_shoot);
220 Time elapsed_time(last_time_requested);
221 elapsed_time -= time_to_shoot;
222 time_so_far += elapsed_time;
223 next_deadline -= first_deadline;
224 time_to_shoot += next_deadline;
225 set_timer(time_to_shoot);
226 }
227 }
228 else {
229 stop_timer();
230 alarm_clock_running = false;
231 }
232 }
233 pending.erase(position);
234 }
235
236 PPL::Implementation::Watchdog::Time PPL::Watchdog::reschedule_time(1);
237
238 void
initialize()239 PPL::Watchdog::initialize() {
240 signal_once.it_interval.tv_sec = 0;
241 signal_once.it_interval.tv_usec = 0;
242
243 sigset_t mask;
244 sigemptyset(&mask);
245
246 struct sigaction s;
247 s.sa_handler = &PPL_handle_timeout;
248 s.sa_mask = mask;
249 s.sa_flags = 0; // Was SA_ONESHOT: why?
250
251 my_sigaction(THE_SIGNAL, &s, 0);
252 }
253
254 void
finalize()255 PPL::Watchdog::finalize() {
256 }
257
258 #endif // PPL_HAVE_DECL_SETITIMER && PPL_HAVE_DECL_SIGACTION
259