/[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 1003 - (show annotations) (download) (as text)
Sun Dec 31 02:35:27 2006 UTC (16 years, 1 month ago) by johnpye
File MIME type: text/x-csrc
File size: 15738 byte(s)
Some progress on fixing test_ascSignal.c
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 /* old signals are *not* stored */
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 break;
276 case SIGSEGV:
277 /* CONSOLE_DEBUG("PUSH SIGSEGV"); */
278 err = push_trap(f_seg_traps, &f_seg_top_of_stack, tp);
279 break;
280 default:
281 return -1;
282 }
283 if(err != 0){
284 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Error from push_trap or fenv_push (err = %d, signal=#%d).",err, signum);
285 return err;
286 }
287 (void)SIGNAL(signum, tp); /* install */
288 return 0;
289 }
290
291 int Asc_SignalHandlerPopDefault(int signum){
292 return Asc_SignalHandlerPop(signum, &Asc_SignalTrap);
293 }
294
295 /* see ascSignal.h */
296 int Asc_SignalHandlerPop(int signum, SigHandlerFn *tp){
297 int err;
298 #ifdef SIGNAL_DEBUG
299 CONSOLE_DEBUG("Popping signal stack for signal %d (expecting top to be %p)",signum,tp);
300 #endif
301
302 switch (signum) {
303 case SIGFPE:
304 CONSOLE_DEBUG("POP SIGFPE");
305 err = pop_trap(f_fpe_traps, &f_fpe_top_of_stack, tp);
306 #if 0 && defined(HAVE_C99FPE)
307 if(!err){
308 err = fenv_pop(f_fenv_stack, &f_fenv_stack_top);
309 }
310 #endif
311 break;
312 case SIGINT:
313 CONSOLE_DEBUG("POP SIGINT");
314 err = pop_trap(f_int_traps, &f_int_top_of_stack, tp);
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 SIGNAL(signum,Asc_SignalStackTop(signum));
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 + 1;
382 case SIGFPE:
383 return f_fpe_top_of_stack + 1;
384 case SIGSEGV:
385 return f_seg_top_of_stack + 1;
386 default:
387 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal %d",signum);
388 return 0;
389 }
390 }
391
392 SigHandlerFn *Asc_SignalStackTop(int signum){
393 SigHandlerFn **stack;
394 int *ptr;
395 switch(signum){
396 case SIGINT:
397 stack = f_int_traps; ptr = &f_int_top_of_stack; break;
398 case SIGFPE:
399 stack = f_fpe_traps; ptr = &f_fpe_top_of_stack; break;
400 case SIGSEGV:
401 stack = f_seg_traps; ptr = &f_seg_top_of_stack; break;
402 }
403 if(stack==NULL)return NULL;
404 if(ptr==NULL)return NULL;
405 if(*ptr < 0) return NULL;
406 if(*ptr >= MAX_TRAP_DEPTH) return NULL;
407 return stack[*ptr];
408 }
409
410 /*------------------------------------------------------------------------------
411 UTILITY FUNCTIONS
412 */
413
414 /*
415 Removed ascresetneeded from here. This is a build-time configuration test
416 rather than a runtime test (and causes annoyance when running ASCEND through
417 a debugger).
418
419 So far the following seem to need reset trapped signals after
420 a longjmp, or unconditionally.
421 HPUX cc -Aa -D_HPUX_SOURCE
422 Solaris cc
423 AIX xlc
424 IRIX cc
425 Windows
426
427 The following retain the last trap set with or without a call to longjmp
428 and so don't need resetting of traps.
429 SunOS4 acc
430 OSF32 cc
431 NetBSD gcc 2.4.5 -ansi
432 Linux gcc (i386)
433 */
434
435 //------------------------------------
436 // COMMOM STACK ROUTINES (shared by the three different signal handler stacks)
437
438 static void initstack(SigHandlerFn **traps, int *stackptr, int sig){
439 SigHandlerFn *old;
440 old = SIGNAL(sig,SIG_DFL);
441 if (old != SIG_ERR && old != SIG_DFL){
442 CONSOLE_DEBUG("Initialising stack for signal %d to %p",sig,old);
443 traps[0] = old;
444 *stackptr = 0;
445 (void)SIGNAL(sig,old);
446 }else{
447 CONSOLE_DEBUG("Initialising stack for signal %d as empty",sig);
448 *stackptr = -1;
449 }
450 }
451
452 static void reset_trap(int signum, SigHandlerFn **tlist, int tos){
453 SigHandlerFn *tp;
454 SigHandlerFn *oldfn;
455 if ((tlist != NULL) && (tos >= 0) && (tos < MAX_TRAP_DEPTH)) {
456 oldfn = signal(signum,SIG_DFL);
457 tp = tlist[tos];
458 if (tp != SIG_ERR) {
459 #ifndef ASC_RESETNEEDED
460 if(tp!=oldfn){
461 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Resetting signal %d (was=%p, new=%p",signum,oldfn,tp);
462 }
463 #endif
464 (void)signal(signum,tp);
465 }
466 }else{
467 (void)SIGNAL(signum,SIG_DFL);
468 }
469 }
470
471 /**
472 Append a pointer to the list given, if the list is not full.
473 */
474 static int push_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp){
475 if (tlist == NULL) {
476 ERROR_REPORTER_HERE(ASC_PROG_ERR,"TLIST is NULL");
477 return -1;
478 }
479 if (stackptr == NULL) {
480 ERROR_REPORTER_HERE(ASC_PROG_ERR,"STACKPTR is NULL");
481 return -1;
482 }
483 if (tp == NULL) {
484 ERROR_REPORTER_HERE(ASC_PROG_ERR,"TP is NULL");
485 return 2;
486 }
487 if (*stackptr > MAX_TRAP_DEPTH-1) {
488 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stackptr >= capacity");
489 return 1;
490 }
491 ++(*stackptr);
492 tlist[*stackptr] = tp;
493 return 0;
494 }
495
496
497 /**
498 @return 0 on success, 2 on NULL tlist or stackptr input, 1 on empty stack
499 or -1 on mismatched input tp and stack data
500
501 Any non-zero return code leaves the stack as it was.
502 */
503 static int pop_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp){
504 SigHandlerFn *oldtrap;
505
506 if ((tlist == NULL) || (stackptr == NULL)) {
507 return 2;
508 }
509 if (*stackptr < 0) {
510 return 1;
511 }
512 oldtrap = tlist[*stackptr];
513 if(oldtrap != tp)return -1;
514 tlist[*stackptr] = NULL;
515 --(*stackptr);
516 return 0;
517 }
518
519 static void print_stack(int signum, SigHandlerFn **tlist, int tos){
520 int i;
521 CONSOLE_DEBUG("---------------");
522 for(i=0;i<=tos;++i){
523 CONSOLE_DEBUG("Signal #%d, stack %d/%d: %p",signum,i,tos,tlist[i]);
524 }
525 CONSOLE_DEBUG("--------------- = %d",tos);
526 }
527
528 /*------------------------------------------
529 FPE ENV STACK
530 */
531
532 #ifdef HAVE_C99FPE
533
534 /**
535 Store current FPU state so that we can reset it later (after we've done
536 some stuff)
537
538 return 0 on success
539 */
540 static int fenv_push(fenv_t *stack, int *top, int excepts){
541 CONSOLE_DEBUG("Pushing FENV flags %d",excepts);
542
543 if(*top > MAX_TRAP_DEPTH - 1){
544 ERROR_REPORTER_HERE(ASC_PROG_ERR,"FPE stack is full");
545 return 1;
546 }
547 if(*top < -1){
548 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack top < -1");
549 return 2;
550 }
551 if(stack==NULL){
552 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack is NULL");
553 return 3;
554 }
555 fenv_t *fe = &stack[++(*top)];
556 if(fegetenv(fe)){
557 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to get env");
558 return 4;
559 }
560 fesetexceptflag(&g_fenv, excepts);
561 //CONSOLE_DEBUG("Enabled div-by-zero FPE exception (%d)",*top);
562 return 0;
563 }
564
565 /**
566 Restore a saved FPU state. Return 0 on success.
567 */
568 static int fenv_pop(fenv_t *stack, int *top){
569 CONSOLE_DEBUG("Popping FENV flags");
570
571 if(*top < 0){
572 ERROR_REPORTER_HERE(ASC_PROG_ERR,"FPE stack is empty");
573 return 1;
574 }
575 if(stack==NULL){
576 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack is NULL");
577 return 2;
578 }
579 fenv_t *fe = &stack[(*top)--];
580 if(fesetenv(fe)){
581 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to set env");
582 return 3;
583 }
584 //CONSOLE_DEBUG("Restorted FPE state");
585 return 0;
586 }
587
588 #endif /* HAVE_C99FPE */
589

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