/[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 1002 - (show annotations) (download) (as text)
Sat Dec 30 14:27:47 2006 UTC (13 years, 11 months ago) by johnpye
File MIME type: text/x-csrc
File size: 15246 byte(s)
Some work on fixing error with test_ascSignal.
Breaking down into smaller test cases.
Removed some debug output from detection of ASC_RESETNEEDED.
Changed all calls 'signal' to 'SIGNAL' macro that includes optional debug output.
Removed 'libasctest.so' (made part of libasctestsuite.so FWIW)
Fixed big in test.c wrt CUEA_ABORT.
Added 'print_stack' and 'Asc_SignalPrintStack' and 'Asc_SignalStackLength'.

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 18 Dec 06 - Removed ascresetneeded (moved to SConstruct)
42 */
43
44 #define _GNU_SOURCE /* enables feenableexcept (http://gcc.gnu.org/ml/fortran/2005-10/msg00365.html) */
45 #include <stdio.h>
46 #include "config.h"
47 #include "ascConfig.h"
48
49 #include <signal.h>
50 #include <setjmp.h>
51
52 #ifdef HAVE_C99FPE
53 # include <fenv.h>
54 #endif
55
56 #ifdef __WIN32__
57 # include <process.h>
58 #else
59 # include <unistd.h>
60 #endif
61
62 #include "ascMalloc.h"
63 #include "ascSignal.h"
64 #include "ascPanic.h"
65
66 #define SIGNAL_DEBUG
67
68 /*------------------------------------------------------------------------------
69 GLOBALS AND FOWARD DECS
70 */
71
72 /* test buf for initialization */
73 JMP_BUF g_fpe_env;
74 JMP_BUF g_seg_env;
75 JMP_BUF g_int_env;
76
77 #ifdef HAVE_C99FPE
78 fenv_t g_fenv;
79 #endif
80
81 /* for future use */
82 jmp_buf g_foreign_code_call_env;
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 #ifdef HAVE_C99FPE
94 static fenv_t *f_fenv_stack = NULL;
95 static int f_fenv_stack_top = -1;
96 #endif
97
98 static void initstack (SigHandlerFn **traps, int *stackptr, int sig);
99 static int pop_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp);
100 static int push_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp);
101 static void reset_trap(int signum, SigHandlerFn **tlist, int tos);
102 static void print_stack(int signum, SigHandlerFn **tlist, int tos);
103
104 #ifdef HAVE_C99FPE
105 static int fenv_pop(fenv_t *stack, int *top);
106 static int fenv_push(fenv_t *stack,int *top, int excepts);
107 #endif
108
109 /*------------------------------------------------------------------------------
110 API FUNCTIONS
111 */
112
113 /**
114 Initialise ASCEND signal handling
115
116 Does not establish any traps, just the structures for maintaining them.
117 Pushes the existing traps, if any, on the bottom of the created stacks.
118
119 @NOTE Cannot be called twice successfully.
120
121 @return 0 if successful, 1 if out of memory, 2 otherwise.
122 */
123 int Asc_SignalInit(void)
124 {
125 /* initialize SIGFPE stack */
126
127 #ifdef SIGNAL_DEBUG
128 CONSOLE_DEBUG("Initialising signal stack");
129 #endif
130
131 if (f_fpe_traps == NULL) {
132 f_fpe_traps = ASC_NEW_ARRAY_CLEAR(SigHandlerFn*,MAX_TRAP_DEPTH);
133 if (f_fpe_traps == NULL) {
134 return 1;
135 }
136 }
137 f_fpe_top_of_stack = -1;
138
139 #ifdef HAVE_C99FPE
140 if(f_fenv_stack==NULL){ /* if we haven't already initialised this... */
141 f_fenv_stack = ASC_NEW_ARRAY_CLEAR(fenv_t,MAX_TRAP_DEPTH);
142 if(f_fenv_stack == NULL){
143 return 1; /* failed to allocate */
144 }
145 }
146 f_fenv_stack_top = -1;
147 #endif
148
149 /* initialize SIGINT stack */
150 if (f_int_traps == NULL) {
151 f_int_traps = ASC_NEW_ARRAY_CLEAR(SigHandlerFn*,MAX_TRAP_DEPTH);
152 if(f_int_traps == NULL){
153 /* failed malloc: free the earlier stuff */
154 ASC_FREE(f_fpe_traps);
155 f_fpe_traps = NULL;
156 return 1;
157 }
158 }
159 f_int_top_of_stack = -1;
160
161 /* initialize SIGSEGV stack */
162 if (f_seg_traps == NULL) {
163 f_seg_traps = ASC_NEW_ARRAY_CLEAR(SigHandlerFn*,MAX_TRAP_DEPTH);
164 if (f_seg_traps == NULL) {
165 /* failed malloc: free the earlier stuff */
166 ASC_FREE(f_fpe_traps);
167 f_fpe_traps = NULL;
168 ASC_FREE(f_int_traps);
169 f_int_traps = NULL;
170 return 1;
171 }
172 }
173 f_seg_top_of_stack = -1;
174
175 /* push the old ones if any, on the stack. */
176 initstack(f_fpe_traps, &f_fpe_top_of_stack, SIGFPE);
177 initstack(f_int_traps, &f_int_top_of_stack, SIGINT);
178 initstack(f_seg_traps, &f_seg_top_of_stack, SIGSEGV);
179
180 #ifdef HAVE_C99FPE
181 CONSOLE_DEBUG("Initialise FPE state to stack (%d)",f_fenv_stack_top);
182 fenv_push(f_fenv_stack,&f_fenv_stack_top,0);
183 #endif
184
185 return 0;
186 }
187
188 /**
189 Clears and destroys the stacks of signal handlers.
190 */
191 void Asc_SignalDestroy(void)
192 {
193 ascfree(f_fpe_traps);
194 ascfree(f_int_traps);
195 ascfree(f_seg_traps);
196 #ifdef HAVE_C99FPE
197 if(f_fenv_stack){
198 ASC_FREE(f_fenv_stack);
199 f_fenv_stack = NULL;
200 }
201 #endif
202 f_fpe_traps = f_int_traps = f_seg_traps = NULL;
203 f_fpe_top_of_stack = f_int_top_of_stack = f_seg_top_of_stack = -1;
204
205 #ifdef SIGNAL_DEBUG
206 CONSOLE_DEBUG("Destroyed signal stack");
207 #endif
208 }
209
210 /**
211 This function reinstalls all the signal handlers this module
212 has been informed of. This should be called after every
213 trapped exception and at any other time when the status of
214 exception handlers may have become not well defined.
215 The most recently pushed handler is installed for each supported
216 signal. If nothing on stack, SIG_DFL gets installed.
217
218 @NOTE that if somebody installs a handler without going through
219 our push/pop, theirs is liable to be forgotten.
220 */
221 void Asc_SignalRecover(int force){
222 #ifndef ASC_RESETNEEDED
223 if(force){
224 #endif
225 # ifdef SIGNAL_DEBUG
226 CONSOLE_DEBUG("Resetting traps");
227 # endif
228 reset_trap(SIGFPE, f_fpe_traps, f_fpe_top_of_stack);
229 reset_trap(SIGINT, f_int_traps, f_int_top_of_stack);
230 reset_trap(SIGSEGV, f_seg_traps, f_seg_top_of_stack);
231 #ifndef ASC_RESETNEEDED
232 }
233 #endif
234 }
235
236 int Asc_SignalHandlerPushDefault(int signum){
237 return Asc_SignalHandlerPush(signum, &Asc_SignalTrap);
238 }
239
240 /**
241 Add a handler to the stack of signal handlers for the given signal.
242
243 There is a maximum stack limit, so returns 1 if limit exceeded.
244 Returns -1 if stack of signal requested does not exist.
245 Pushing a NULL handler does NOT change anything at all.
246 On a successful return, the handler has been installed and will
247 remain installed until a Asc_SignalHandlerPop or another push.
248 */
249 int Asc_SignalHandlerPush(int signum, SigHandlerFn *tp)
250 {
251 int err;
252 if (tp == NULL) {
253 return 0;
254 }
255
256 #ifdef SIGNAL_DEBUG
257 CONSOLE_DEBUG("Pushing handler at %p for signal %d",tp,signum);
258 #endif
259
260 switch (signum) {
261 case SIGFPE:
262 //CONSOLE_DEBUG("PUSH SIGFPE");
263 err = push_trap(f_fpe_traps, &f_fpe_top_of_stack, tp);
264 #ifdef HAVE_C99FPE
265 if(tp == SIG_IGN){
266 err = fenv_push(f_fenv_stack, &f_fenv_stack_top,FE_ALL_EXCEPT);
267 }else{
268 err = fenv_push(f_fenv_stack, &f_fenv_stack_top,0);
269 }
270 #endif
271 break;
272 case SIGINT:
273 CONSOLE_DEBUG("PUSH SIGINT");
274 err = push_trap(f_int_traps, &f_int_top_of_stack, tp);
275 print_stack(SIGINT,f_int_traps,f_int_top_of_stack);
276 break;
277 case SIGSEGV:
278 /* CONSOLE_DEBUG("PUSH SIGSEGV"); */
279 err = push_trap(f_seg_traps, &f_seg_top_of_stack, tp);
280 break;
281 default:
282 return -1;
283 }
284 if (err != 0) {
285 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Error from push_trap or fenv_push (err = %d, signal=#%d).",err, signum);
286 return err;
287 }
288 (void)SIGNAL(signum, tp); /* install */
289 return 0;
290 }
291
292 int Asc_SignalHandlerPopDefault(int signum){
293 return Asc_SignalHandlerPop(signum, &Asc_SignalTrap);
294 }
295
296 /* see ascSignal.h */
297 int Asc_SignalHandlerPop(int signum, SigHandlerFn *tp){
298 int err;
299 #ifdef SIGNAL_DEBUG
300 CONSOLE_DEBUG("Popping signal stack for signal %d (expecting top to be %p)",signum,tp);
301 #endif
302
303 switch (signum) {
304 case SIGFPE:
305 //CONSOLE_DEBUG("POP SIGFPE");
306 err = pop_trap(f_fpe_traps, &f_fpe_top_of_stack, tp);
307 #ifdef HAVE_C99FPE
308 err = fenv_pop(f_fenv_stack, &f_fenv_stack_top);
309 #endif
310 break;
311 case SIGINT:
312 CONSOLE_DEBUG("POP SIGINT");
313 err = pop_trap(f_int_traps, &f_int_top_of_stack, tp);
314 print_stack(SIGINT,f_int_traps,f_int_top_of_stack);
315 break;
316 case SIGSEGV:
317 /* CONSOLE_DEBUG("POP SIGSEGV"); */
318 err = pop_trap(f_seg_traps, &f_seg_top_of_stack, tp);
319 break;
320 default:
321 CONSOLE_DEBUG("popping invalid signal type (signum = %d)", signum);
322 return -1;
323 }
324 if (err != 0 && tp != NULL) {
325 CONSOLE_DEBUG("stack pop mismatch");
326 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Asc_Signal (%d) stack pop mismatch.",signum);
327 return err;
328 }
329 Asc_SignalRecover(TRUE);
330 return 0;
331 }
332
333 void Asc_SignalTrap(int sigval) {
334 #ifdef SIGNAL_DEBUG
335 CONSOLE_DEBUG("Caught signal #%d",sigval);
336 #endif
337 switch(sigval) {
338 case SIGFPE:
339 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Floating point error caught");
340 CONSOLE_DEBUG("SIGFPE caught");
341 #ifdef HAVE_C99FPE
342 FPRESET;
343 #endif
344 LONGJMP(g_fpe_env,sigval);
345 break;
346 case SIGINT:
347 CONSOLE_DEBUG("SIGINT (Ctrl-C) caught");
348 LONGJMP(g_int_env,sigval);
349 break;
350 case SIGSEGV:
351 CONSOLE_DEBUG("SIGSEGV caught");
352 LONGJMP(g_seg_env,sigval);
353 break;
354 default:
355 CONSOLE_DEBUG("Installed on unexpected signal (sigval = %d).", sigval);
356 CONSOLE_DEBUG("Returning ... who knows where :-)");
357 break;
358 }
359 return;
360 }
361
362 void Asc_SignalPrintStack(int signum){
363 switch(signum){
364 case SIGINT:
365 print_stack(SIGINT,f_int_traps,f_int_top_of_stack);
366 return;
367 case SIGFPE:
368 print_stack(SIGFPE,f_fpe_traps,f_fpe_top_of_stack);
369 return;
370 case SIGSEGV:
371 print_stack(SIGSEGV,f_seg_traps,f_seg_top_of_stack);
372 return;
373 default:
374 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal %d",signum);
375 }
376 }
377
378 int Asc_SignalStackLength(int signum){
379 switch(signum){
380 case SIGINT:
381 return f_int_top_of_stack;
382 case SIGFPE:
383 return f_fpe_top_of_stack;
384 case SIGSEGV:
385 return f_seg_top_of_stack;
386 default:
387 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal %d",signum);
388 }
389 }
390
391
392 /*------------------------------------------------------------------------------
393 UTILITY FUNCTIONS
394 */
395
396 /*
397 Removed ascresetneeded from here. This is a build-time configuration test
398 rather than a runtime test (and causes annoyance when running ASCEND through
399 a debugger).
400
401 So far the following seem to need reset trapped signals after
402 a longjmp, or unconditionally.
403 HPUX cc -Aa -D_HPUX_SOURCE
404 Solaris cc
405 AIX xlc
406 IRIX cc
407 Windows
408
409 The following retain the last trap set with or without a call to longjmp
410 and so don't need resetting of traps.
411 SunOS4 acc
412 OSF32 cc
413 NetBSD gcc 2.4.5 -ansi
414 Linux gcc (i386)
415 */
416
417 //------------------------------------
418 // COMMOM STACK ROUTINES (shared by the three different signal handler stacks)
419
420 static void initstack(SigHandlerFn **traps, int *stackptr, int sig){
421 SigHandlerFn *old;
422 old = SIGNAL(sig,SIG_DFL);
423 if (old != SIG_ERR && old != SIG_DFL){
424 CONSOLE_DEBUG("Initialising stack for signal %d to %p",sig,old);
425 traps[0] = old;
426 *stackptr = 0;
427 (void)SIGNAL(sig,old);
428 }
429 }
430
431 static void reset_trap(int signum, SigHandlerFn **tlist, int tos){
432 SigHandlerFn *tp;
433 SigHandlerFn *oldfn;
434 if ((tlist != NULL) && (tos >= 0) && (tos < MAX_TRAP_DEPTH)) {
435 oldfn = SIGNAL(signum,SIG_DFL);
436 tp = tlist[tos];
437 if (tp != SIG_ERR) {
438 #ifdef ASC_RESETNEEDED
439 # ifdef SIGNAL_DEBUG
440 CONSOLE_DEBUG("Resetting signal #%d (was %p, new %p)",signum,oldfn,tp);
441 # endif
442 (void)SIGNAL(signum,tp);
443 #else
444 # ifdef SIGNAL_DEBUG
445 if(tp!=oldfn){
446 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Resetting signal %d (shouldn't be necessary)",signum);
447 (void)SIGNAL(signum,tp);
448 }
449 # endif
450 #endif
451 }
452 }else{
453 (void)SIGNAL(signum,SIG_DFL);
454 }
455 }
456
457 /**
458 Append a pointer to the list given, if the list is not full.
459 */
460 static int push_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp){
461 if (tlist == NULL) {
462 CONSOLE_DEBUG("TLIST IS NULL");
463 return -1;
464 }
465 if (stackptr == NULL) {
466 CONSOLE_DEBUG("STACKPTR IS NULL");
467 return -1;
468 }
469 if (tp == NULL) {
470 CONSOLE_DEBUG("TP IS NULL");
471 return 2;
472 }
473 if (*stackptr > MAX_TRAP_DEPTH-1) {
474 CONSOLE_DEBUG("TLIST LENGTH = CAPACITY");
475 return 1;
476 }
477 ++(*stackptr);
478 tlist[*stackptr] = tp;
479 return 0;
480 }
481
482
483 /*
484 Returns: 0 -ok, 2 NULL list input, 1 empty list input,
485 -1 mismatched input tp and stack data.
486 */
487 static int pop_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp){
488 SigHandlerFn *oldtrap;
489
490 if ((tlist == NULL) || (stackptr == NULL)) {
491 return 2;
492 }
493 if (*stackptr < 0) {
494 return 1;
495 }
496 oldtrap = tlist[*stackptr];
497 tlist[*stackptr] = NULL;
498 --(*stackptr);
499 return (-(oldtrap != tp));
500 }
501
502 static void print_stack(int signum, SigHandlerFn **tlist, int tos){
503 int i;
504 CONSOLE_DEBUG("---------------");
505 for(i=0;i<tos;++i){
506 CONSOLE_DEBUG("Signal #%d, stack %d/%d: %p",signum,i,tos,tlist[i]);
507 }
508 CONSOLE_DEBUG("--------------- = %d",tos);
509 }
510
511 /*------------------------------------------
512 FPE ENV STACK
513 */
514
515 #ifdef HAVE_C99FPE
516
517 /**
518 Store current FPU state so that we can reset it later (after we've done
519 some stuff)
520
521 return 0 on success
522 */
523 static int fenv_push(fenv_t *stack, int *top, int excepts){
524 CONSOLE_DEBUG("Pushing FENV flags %d",excepts);
525
526 if(*top > MAX_TRAP_DEPTH - 1){
527 ERROR_REPORTER_HERE(ASC_PROG_ERR,"FPE stack is full");
528 return 1;
529 }
530 if(*top < -1){
531 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack top < -1");
532 return 2;
533 }
534 if(stack==NULL){
535 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack is NULL");
536 return 3;
537 }
538 fenv_t *fe = &stack[++(*top)];
539 if(fegetenv(fe)){
540 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to get env");
541 return 4;
542 }
543 fesetexceptflag(&g_fenv, excepts);
544 //CONSOLE_DEBUG("Enabled div-by-zero FPE exception (%d)",*top);
545 return 0;
546 }
547
548 /**
549 Restore a saved FPU state. Return 0 on success.
550 */
551 static int fenv_pop(fenv_t *stack, int *top){
552 CONSOLE_DEBUG("Popping FENV flags");
553
554 if(*top < 0){
555 ERROR_REPORTER_HERE(ASC_PROG_ERR,"FPE stack is empty");
556 return 1;
557 }
558 if(stack==NULL){
559 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack is NULL");
560 return 2;
561 }
562 fenv_t *fe = &stack[(*top)--];
563 if(fesetenv(fe)){
564 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to set env");
565 return 3;
566 }
567 //CONSOLE_DEBUG("Restorted FPE state");
568 return 0;
569 }
570
571 #endif /* HAVE_C99FPE */
572

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