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, &current_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