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

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