/[ascend]/trunk/base/generic/utilities/ascSignal.c
ViewVC logotype

Contents of /trunk/base/generic/utilities/ascSignal.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 953 - (show annotations) (download) (as text)
Thu Dec 7 14:47:15 2006 UTC (17 years, 10 months ago) by johnpye
File MIME type: text/x-csrc
File size: 12559 byte(s)
Added test for C99 FPE handling
Fixing mess-up of ChildByChar in arrayinst.h header.
Added 'safeeval' config option to IDA.
Changed 'SigHandler' to 'SigHandlerFn *' in line with other function pointer datatypes being used in ASCEND.
Moved processVarStatus *after* 'Failed integrator' exception (ongoing issue).
1 /* ASCEND modelling environment
2 Copyright (C) 1997 Benjamin Andrew Allan
3 Copyright (C) 2006 Carnegie Mellon University
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2, or (at your option)
8 any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 *//** @file
20 Signal handling protocol definitions for ASCEND
21
22 Start of making signal handling in ASCEND
23 code somewhat sane. Still needs to somehow
24 support the management of jmp_buf's so that
25 handlers will longjmp to the right place.
26
27 A better alternative is to make all our code check/return
28 status flags based on a variable set by the trap
29 and cleared by recipient.
30 *//*
31 by Benjamin Andrew Allan, May 27, 1997
32 Last in CVS $Revision: 1.9 $ $Date: 1999/01/19 12:23:20 $ $Author: mthomas $
33
34 ChangeLog
35
36 10/15/2005 - Changed ascresetneeded() so that any previously
37 registered handlers are reset before return.
38 - Added Asc_SignalRecover() to standard handler
39 Asc_SignalTrap() so handlers are reset if necessary. (JDS)
40 12/10/2005 - Changed storage of signal handlers from gl_list's
41 to local arrays. gl_list's can't legally hold
42 function pointers. (JDS)
43 */
44
45 #include <stdio.h>
46 #include "ascConfig.h"
47
48 #ifndef NO_SIGNAL_TRAPS
49 # include <signal.h>
50 # include <setjmp.h>
51 #endif /* NO_SIGNAL_TRAPS*/
52
53 #ifdef __WIN32__
54 # include <process.h>
55 #else
56 # include <unistd.h>
57 #endif
58
59 #include "ascMalloc.h"
60 #include "ascSignal.h"
61
62 #if !defined(NO_SIGINT_TRAP) || !defined(NO_SIGSEGV_TRAP)
63 static jmp_buf f_test_env; /* for local testing of signal handling */
64 #endif
65
66 #ifndef NO_SIGNAL_TRAPS
67 /* test buf for initialization */
68 jmp_buf g_fpe_env;
69 jmp_buf g_seg_env;
70 jmp_buf g_int_env;
71
72 /* for future use */
73 jmp_buf g_foreign_code_call_env;
74
75 #endif /* NO_SIGNAL_TRAPS*/
76
77 static int f_reset_needed = -2;
78 /* has value 0 or 1 after Init is called.
79 * and if Init is called without the value -2 in f_reset_needed,
80 * it will fail.
81 */
82 static SigHandlerFn **f_fpe_traps = NULL; /**< array for pushed SIGFPE handlers */
83 static int f_fpe_top_of_stack = -1; /**< top of SIGFPE stack, -1 for empty */
84
85 static SigHandlerFn **f_int_traps = NULL; /**< array for pushed SIGINT handlers */
86 static int f_int_top_of_stack = -1; /**< top of SIGFPE stack, -1 for empty */
87
88 static SigHandlerFn **f_seg_traps = NULL; /**< array for pushed SIGSEGV handlers */
89 static int f_seg_top_of_stack = -1; /**< top of SIGFPE stack, -1 for empty */
90
91 #ifndef NO_SIGSEGV_TRAP
92 /* function to throw an interrupt. system dependent. */
93 static int testdooley2(int sig){
94 raise(sig);
95 return 0;
96 }
97 #endif
98
99 #if !defined(NO_SIGINT_TRAP) || !defined(NO_SIGSEGV_TRAP)
100 /* function to catch an interrupt */
101 static void testcatch(int signum){
102 FPRINTF(ASCERR," signal %d caught ",signum);
103 if (signum == SIGFPE) {
104 FPRESET;
105 }
106 longjmp(f_test_env, signum);
107 }
108 #endif
109
110 /*
111 * So far the following seem to need reset trapped signals after
112 * a longjmp, or unconditionally.
113 * HPUX cc -Aa -D_HPUX_SOURCE
114 * Solaris cc
115 * AIX xlc
116 * IRIX cc
117 * Windows
118 *
119 * The following retain the last trap set with or without a call to longjmp
120 * and so don't need resetting of traps.
121 * SunOS4 acc
122 * OSF32 cc
123 * NetBSD gcc 2.4.5 -ansi
124 */
125
126 /**
127 This function tests the signal reseting of compilers using SIGINT.
128 It should not be called except when starting a process.
129
130 Side effects:
131 - a line is sent to ASCERR
132 - SIGINT is set to SIG_DFL if no handler was previously registered
133 - SIGFPE may be set to SIG_DFL if no handler was previously registered
134
135 @return 0 for no reset needed, 1 for reset needed, and
136 -1 if the test fails (presuming program doesn't exit first.)
137 */
138 static int ascresetneeded(void) {
139 static int result = 0;
140
141 #if !defined(NO_SIGINT_TRAP) || !defined(NO_SIGSEGV_TRAP)
142 SigHandlerFn *lasttrap;
143 volatile SigHandlerFn *savedtrap;
144 static int c=0;
145 #endif
146
147 #ifndef NO_SIGINT_TRAP
148
149 /* test interrupt */
150 savedtrap = signal(SIGINT, testcatch);
151 CONSOLE_DEBUG("Testing signal SIGINT (signum = %d) %p\t%p\t", SIGINT, savedtrap, testcatch);
152 if (setjmp(f_test_env) == 0) {
153 testdooley2(SIGINT);
154 } else {
155 c++;
156 }
157 if (c != 1) {
158 CONSOLE_DEBUG("SIGINT test failed");
159 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"Signal (SIGINT) test failed. ASCEND unlikely to work on this hardware.");
160 result = -1;
161 }
162 lasttrap = signal(SIGINT, (NULL != savedtrap) ? savedtrap : SIG_DFL);
163 CONSOLE_DEBUG("%p",lasttrap);
164 if (lasttrap != testcatch) {
165 result = 1;
166 }
167
168 if (result != 0) {
169 return result;
170 }
171
172 c = 0;
173 #else
174 CONSOLE_DEBUG("SIGINT trap bypassed: compile-time settings");
175 #endif
176
177 #ifndef NO_SIGSEGV_TRAP
178 /* passed interrupt, check fpe */
179 savedtrap=signal(SIGFPE, testcatch);
180 CONSOLE_DEBUG("Testing signal %d %p\t%p\t",SIGFPE, savedtrap, testcatch);
181 if (setjmp(f_test_env)==0) {
182 testdooley2(SIGFPE);
183 } else {
184 c++;
185 }
186 if (c != 1) {
187 CONSOLE_DEBUG("SIGFPE test failed");
188 ERROR_REPORTER_NOLINE(ASC_PROG_ERROR,"Signal test failed. ASCEND unlikely to work on this hardware.");
189 result = -1;
190 }
191 lasttrap = signal(SIGFPE, (NULL != savedtrap) ? savedtrap : SIG_DFL);
192 CONSOLE_DEBUG("%p\n",lasttrap);
193 if (lasttrap != testcatch) {
194 result = 1;
195 }
196 #else
197 CONSOLE_DEBUG("SIGSEGV trap bypassed: compile-time settings.");
198 #endif
199
200 return result;
201 }
202
203 static void initstack (SigHandlerFn **traps, int *stackptr, int sig){
204 SigHandlerFn *old;
205 old = signal(sig,SIG_DFL);
206 if (old != SIG_ERR && old != SIG_DFL) {
207 traps[0] = old;
208 *stackptr = 0;
209 (void)signal(sig,old);
210 }
211 }
212
213 /**
214 Initialise ASCEND signal handling
215
216 Does not establish any traps, just the structures for maintaining them.
217 Pushes the existing traps, if any, on the bottom of the created stacks.
218
219 @NOTE Cannot be called twice successfully.
220
221 @return 0 if successful, 1 if out of memory, 2 otherwise.
222 */
223 int Asc_SignalInit(void)
224 {
225 if (f_reset_needed != -2) {
226 return 2;
227 }
228
229 /* initialize SIGFPE stack */
230 if (f_fpe_traps == NULL) {
231 f_fpe_traps = ASC_NEW_ARRAY_CLEAR(SigHandlerFn*,MAX_TRAP_DEPTH);
232 if (f_fpe_traps == NULL) {
233 return 1;
234 }
235 }
236 f_fpe_top_of_stack = -1;
237
238 /* initialize SIGINT stack */
239 if (f_int_traps == NULL) {
240 f_int_traps = ASC_NEW_ARRAY_CLEAR(SigHandlerFn*,MAX_TRAP_DEPTH);
241 if (f_int_traps == NULL) {
242 ascfree(f_fpe_traps);
243 f_fpe_traps = NULL;
244 return 1;
245 }
246 }
247 f_int_top_of_stack = -1;
248
249 /* initialize SIGSEGV stack */
250 if (f_seg_traps == NULL) {
251 f_seg_traps = ASC_NEW_ARRAY_CLEAR(SigHandlerFn*,MAX_TRAP_DEPTH);
252 if (f_seg_traps == NULL) {
253 ascfree(f_fpe_traps);
254 f_fpe_traps = NULL;
255 ascfree(f_int_traps);
256 f_int_traps = NULL;
257 return 1;
258 }
259 }
260 f_seg_top_of_stack = -1;
261
262 #ifndef NO_SIGNAL_TRAPS
263 /* push the old ones if any, on the stack. */
264 initstack(f_fpe_traps, &f_fpe_top_of_stack, SIGFPE);
265
266 # ifndef NO_SIGINT_TRAP
267 initstack(f_int_traps, &f_int_top_of_stack, SIGINT);
268 # endif
269
270 # ifndef NO_SIGSEGV_TRAP
271 initstack(f_seg_traps, &f_seg_top_of_stack, SIGSEGV);
272 # endif
273
274 f_reset_needed = ascresetneeded();
275 if (f_reset_needed < 0) {
276 f_reset_needed = 1;
277 return 2;
278 }
279 #endif /* NO_SIGNAL_TRAPS */
280 return 0;
281 }
282
283 /**
284 Clears and destroys the stacks of signal handlers.
285 */
286 void Asc_SignalDestroy(void)
287 {
288 ascfree(f_fpe_traps);
289 ascfree(f_int_traps);
290 ascfree(f_seg_traps);
291 f_fpe_traps = f_int_traps = f_seg_traps = NULL;
292 f_fpe_top_of_stack = f_int_top_of_stack = f_seg_top_of_stack = -1;
293 }
294
295 static void reset_trap(int signum, SigHandlerFn **tlist, int tos)
296 {
297 SigHandlerFn *tp;
298 if ((tlist != NULL) && (tos >= 0) && (tos < MAX_TRAP_DEPTH)) {
299 tp = tlist[tos];
300 if (tp != SIG_ERR) {
301 (void)signal(signum,tp);
302 }
303 } else {
304 (void)signal(signum,SIG_DFL);
305 }
306 }
307 /*
308 This function reinstalls all the signal handlers this module
309 has been informed of. This should be called after every
310 trapped exception and at any other time when the status of
311 exception handlers may have become not well defined.
312 The most recently pushed handler is installed for each supported
313 signal. If nothing on stack, SIG_DFL gets installed.
314
315 @NOTE that if somebody installs a handler without going through
316 our push/pop, theirs is liable to be forgotten.
317 */
318 void Asc_SignalRecover(int force)
319 {
320 if (force || f_reset_needed > 0) {
321 #ifndef NO_SIGNAL_TRAPS
322 reset_trap(SIGFPE, f_fpe_traps, f_fpe_top_of_stack);
323 reset_trap(SIGINT, f_int_traps, f_int_top_of_stack);
324 reset_trap(SIGSEGV, f_seg_traps, f_seg_top_of_stack);
325 #endif /* NO_SIGNAL_TRAPS */
326 }
327 }
328
329 /**
330 Append a pointer to the list given, if the list is not full.
331 */
332 static int push_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp)
333 {
334 if (tlist == NULL) {
335 CONSOLE_DEBUG("TLIST IS NULL");
336 return -1;
337 }
338 if (stackptr == NULL) {
339 CONSOLE_DEBUG("STACKPTR IS NULL");
340 return -1;
341 }
342 if (tp == NULL) {
343 CONSOLE_DEBUG("TP IS NULL");
344 return 2;
345 }
346 if (*stackptr > MAX_TRAP_DEPTH-1) {
347 CONSOLE_DEBUG("TLIST LENGTH = CAPACITY");
348 return 1;
349 }
350 ++(*stackptr);
351 tlist[*stackptr] = tp;
352 return 0;
353 }
354
355 /*
356 Add a handler to the stack of signal handlers for the given signal.
357
358 There is a maximum stack limit, so returns 1 if limit exceeded.
359 Returns -1 if stack of signal requested does not exist.
360 Pushing a NULL handler does NOT change anything at all.
361 On a successful return, the handler has been installed and will
362 remain installed until a Asc_SignalHandlerPop or another push.
363 */
364 int Asc_SignalHandlerPush(int signum, SigHandlerFn *tp)
365 {
366 int err;
367 if (tp == NULL) {
368 return 0;
369 }
370 switch (signum) {
371 case SIGFPE:
372 /*ERROR_REPORTER_DEBUG("PUSH SIGFPE");*/
373 err = push_trap(f_fpe_traps, &f_fpe_top_of_stack, tp);
374 break;
375 case SIGINT:
376 /*ERROR_REPORTER_DEBUG("PUSH SIGINT");*/
377 err = push_trap(f_int_traps, &f_int_top_of_stack, tp);
378 break;
379 case SIGSEGV:
380 /*ERROR_REPORTER_DEBUG("PUSH SIGSEGV");*/
381 err = push_trap(f_seg_traps, &f_seg_top_of_stack, tp);
382 break;
383 default:
384 return -1;
385 }
386 if (err != 0) {
387 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Asc_Signal (%d) stack limit exceeded.",signum);
388 return err;
389 }
390 (void)signal(signum, tp); /* install */
391 return 0;
392 }
393
394 /*
395 Returns: 0 -ok, 2 NULL list input, 1 empty list input,
396 -1 mismatched input tp and stack data.
397 */
398 static int pop_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp){
399 SigHandlerFn *oldtrap;
400
401 if ((tlist == NULL) || (stackptr == NULL)) {
402 return 2;
403 }
404 if (*stackptr < 0) {
405 return 1;
406 }
407 oldtrap = tlist[*stackptr];
408 tlist[*stackptr] = NULL;
409 --(*stackptr);
410 return (-(oldtrap != tp));
411 }
412
413 int Asc_SignalHandlerPop(int signum, SigHandlerFn *tp){
414 int err;
415 switch (signum) {
416 case SIGFPE:
417 /*ERROR_REPORTER_DEBUG("POP SIGFPE");*/
418 err = pop_trap(f_fpe_traps, &f_fpe_top_of_stack, tp);
419 break;
420 case SIGINT:
421 /*ERROR_REPORTER_DEBUG("POP SIGINT");*/
422 err = pop_trap(f_int_traps, &f_int_top_of_stack, tp);
423 break;
424 case SIGSEGV:
425 /*ERROR_REPORTER_DEBUG("POP SIGSEGV");*/
426 err = pop_trap(f_seg_traps, &f_seg_top_of_stack, tp);
427 break;
428 default:
429 CONSOLE_DEBUG("popping invalid signal type (signum = %d)", signum);
430 return -1;
431 }
432 if (err != 0 && tp != NULL) {
433 CONSOLE_DEBUG("stack pop mismatch");
434 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Asc_Signal (%d) stack pop mismatch.",signum);
435 return err;
436 }
437 Asc_SignalRecover(TRUE);
438 return 0;
439 }
440
441 void Asc_SignalTrap(int sigval) {
442 #ifndef NO_SIGNAL_TRAPS
443 switch(sigval) {
444 case SIGFPE:
445 CONSOLE_DEBUG("SIGFPE caught");
446 FPRESET;
447 longjmp(g_fpe_env,sigval);
448 break;
449 case SIGINT:
450 CONSOLE_DEBUG("SIGINT (Ctrl-C) caught");
451 longjmp(g_int_env,sigval);
452 break;
453 case SIGSEGV:
454 CONSOLE_DEBUG("SIGSEGV caught");
455 longjmp(g_seg_env,sigval);
456 break;
457 default:
458 CONSOLE_DEBUG("Installed on unexpected signal (sigval = %d).", sigval);
459 CONSOLE_DEBUG("Returning ... who knows where :-)");
460 break;
461 }
462 return;
463 #else
464 UNUSED_PARAMETER(sigval);
465 #endif /* NO_SIGNAL_TRAPS */
466 }

john.pye@anu.edu.au
ViewVC Help
Powered by ViewVC 1.1.22