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

Contents of /trunk/ascend/utilities/ascSignal.c

Parent Directory Parent Directory | Revision Log Revision Log


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

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