/[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 1008 - (show annotations) (download) (as text)
Tue Jan 2 11:45:24 2007 UTC (13 years, 1 month ago) by johnpye
File MIME type: text/x-csrc
File size: 16159 byte(s)
Fixed bug with memory deallocation for partitioner in ida.c
Silenced some debug output in ascSignal.c
Fixed execution of Python test suite (CUnit tests will need more work before running via this approach)
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 #if 0 && defined(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 @return 0 on success, -2 if tp is NULL, -1 if unsupported signal is given,
250 -3 if 'signal' returns SIG_ERR.
251 */
252 int Asc_SignalHandlerPush(int signum, SigHandlerFn *tp)
253 {
254 int err;
255 if (tp == NULL) {
256 return -2;
257 }
258
259 #ifdef SIGNAL_DEBUG
260 CONSOLE_DEBUG("Pushing handler at %p for signal %d",tp,signum);
261 #endif
262
263 switch (signum) {
264 case SIGFPE:
265 //CONSOLE_DEBUG("PUSH SIGFPE");
266 err = push_trap(f_fpe_traps, &f_fpe_top_of_stack, tp);
267 #if 0 && defined(HAVE_C99FPE)
268 if(tp == SIG_IGN){
269 err = fenv_push(f_fenv_stack, &f_fenv_stack_top,FE_ALL_EXCEPT);
270 }else{
271 err = fenv_push(f_fenv_stack, &f_fenv_stack_top,0);
272 }
273 #endif
274 break;
275 case SIGINT:
276 CONSOLE_DEBUG("PUSH SIGINT");
277 err = push_trap(f_int_traps, &f_int_top_of_stack, tp);
278 break;
279 case SIGSEGV:
280 /* CONSOLE_DEBUG("PUSH SIGSEGV"); */
281 err = push_trap(f_seg_traps, &f_seg_top_of_stack, tp);
282 break;
283 default:
284 return -1;
285 }
286 if(err != 0){
287 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Error from push_trap or fenv_push (err = %d, signal=#%d).",err, signum);
288 return err;
289 }
290 return SIG_ERR==SIGNAL(signum, tp); /* install */
291 }
292
293 int Asc_SignalHandlerPopDefault(int signum){
294 return Asc_SignalHandlerPop(signum, &Asc_SignalTrap);
295 }
296
297 /* see ascSignal.h */
298 int Asc_SignalHandlerPop(int signum, SigHandlerFn *tp){
299 int err;
300 #ifdef SIGNAL_DEBUG
301 CONSOLE_DEBUG("Popping signal stack for signal %d (expecting top to be %p)",signum,tp);
302 #endif
303
304 switch (signum) {
305 case SIGFPE:
306 #ifdef SIGNAL_DEBUG
307 CONSOLE_DEBUG("POP SIGFPE");
308 #endif
309 err = pop_trap(f_fpe_traps, &f_fpe_top_of_stack, tp);
310 #if 0 && defined(HAVE_C99FPE)
311 if(!err){
312 err = fenv_pop(f_fenv_stack, &f_fenv_stack_top);
313 }
314 #endif
315 break;
316 case SIGINT:
317 #ifdef SIGNAL_DEBUG
318 CONSOLE_DEBUG("POP SIGINT");
319 #endif
320 err = pop_trap(f_int_traps, &f_int_top_of_stack, tp);
321 break;
322 case SIGSEGV:
323 #ifdef SIGNAL_DEBUG
324 CONSOLE_DEBUG("POP SIGSEGV");
325 #endif
326 err = pop_trap(f_seg_traps, &f_seg_top_of_stack, tp);
327 break;
328 default:
329 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Popping invalid signal type (signum = %d)", signum);
330 return -1;
331 }
332 if (err != 0 && tp != NULL) {
333 #ifdef SIGNAL_DEBUG
334 CONSOLE_DEBUG("stack pop mismatch");
335 #endif
336 ERROR_REPORTER_HERE(ASC_PROG_ERROR,"Asc_Signal (%d) stack pop mismatch.",signum);
337 return err;
338 }
339 SIGNAL(signum,Asc_SignalStackTop(signum));
340 return 0;
341 }
342
343 void Asc_SignalTrap(int sigval){
344 #ifdef SIGNAL_DEBUG
345 CONSOLE_DEBUG("Caught signal #%d",sigval);
346 #endif
347 switch(sigval) {
348 case SIGFPE:
349 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Floating point error caught");
350 CONSOLE_DEBUG("SIGFPE caught");
351 #ifdef HAVE_C99FPE
352 FPRESET;
353 #endif
354 LONGJMP(g_fpe_env,sigval);
355 break;
356 case SIGINT:
357 CONSOLE_DEBUG("SIGINT (Ctrl-C) caught");
358 LONGJMP(g_int_env,sigval);
359 break;
360 case SIGSEGV:
361 CONSOLE_DEBUG("SIGSEGV caught");
362 LONGJMP(g_seg_env,sigval);
363 break;
364 default:
365 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Installed on unexpected signal (sigval = %d). Returning (who knows where...)", sigval);
366
367 break;
368 }
369 CONSOLE_DEBUG("Returning ... who knows where :-)");
370 return;
371 }
372
373 void Asc_SignalPrintStack(int signum){
374 switch(signum){
375 case SIGINT:
376 print_stack(SIGINT,f_int_traps,f_int_top_of_stack);
377 return;
378 case SIGFPE:
379 print_stack(SIGFPE,f_fpe_traps,f_fpe_top_of_stack);
380 return;
381 case SIGSEGV:
382 print_stack(SIGSEGV,f_seg_traps,f_seg_top_of_stack);
383 return;
384 default:
385 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal %d",signum);
386 }
387 }
388
389 int Asc_SignalStackLength(int signum){
390 switch(signum){
391 case SIGINT:
392 return f_int_top_of_stack + 1;
393 case SIGFPE:
394 return f_fpe_top_of_stack + 1;
395 case SIGSEGV:
396 return f_seg_top_of_stack + 1;
397 default:
398 ERROR_REPORTER_HERE(ASC_PROG_ERR,"Invalid signal %d",signum);
399 return 0;
400 }
401 }
402
403 SigHandlerFn *Asc_SignalStackTop(int signum){
404 SigHandlerFn **stack;
405 int *ptr;
406 switch(signum){
407 case SIGINT:
408 stack = f_int_traps; ptr = &f_int_top_of_stack; break;
409 case SIGFPE:
410 stack = f_fpe_traps; ptr = &f_fpe_top_of_stack; break;
411 case SIGSEGV:
412 stack = f_seg_traps; ptr = &f_seg_top_of_stack; break;
413 }
414 if(stack==NULL)return NULL;
415 if(ptr==NULL)return NULL;
416 if(*ptr < 0) return NULL;
417 if(*ptr >= MAX_TRAP_DEPTH) return NULL;
418 return stack[*ptr];
419 }
420
421 /*------------------------------------------------------------------------------
422 UTILITY FUNCTIONS
423 */
424
425 /*
426 Removed ascresetneeded from here. This is a build-time configuration test
427 rather than a runtime test (and causes annoyance when running ASCEND through
428 a debugger).
429
430 So far the following seem to need reset trapped signals after
431 a longjmp, or unconditionally.
432 HPUX cc -Aa -D_HPUX_SOURCE
433 Solaris cc
434 AIX xlc
435 IRIX cc
436 Windows
437
438 The following retain the last trap set with or without a call to longjmp
439 and so don't need resetting of traps.
440 SunOS4 acc
441 OSF32 cc
442 NetBSD gcc 2.4.5 -ansi
443 Linux gcc (i386)
444 */
445
446 //------------------------------------
447 // COMMOM STACK ROUTINES (shared by the three different signal handler stacks)
448
449 static void initstack(SigHandlerFn **traps, int *stackptr, int sig){
450 SigHandlerFn *old;
451 old = SIGNAL(sig,SIG_DFL);
452 if (old != SIG_ERR && old != SIG_DFL){
453 #ifdef SIGNAL_DEBUG
454 CONSOLE_DEBUG("Initialising stack for signal %d to %p",sig,old);
455 #endif
456 traps[0] = old;
457 *stackptr = 0;
458 (void)SIGNAL(sig,old);
459 }else{
460 #ifdef SIGNAL_DEBUG
461 CONSOLE_DEBUG("Initialising stack for signal %d as empty",sig);
462 #endif
463 *stackptr = -1;
464 }
465 }
466
467 static void reset_trap(int signum, SigHandlerFn **tlist, int tos){
468 SigHandlerFn *tp;
469 SigHandlerFn *oldfn;
470 if ((tlist != NULL) && (tos >= 0) && (tos < MAX_TRAP_DEPTH)) {
471 oldfn = signal(signum,SIG_DFL);
472 tp = tlist[tos];
473 if (tp != SIG_ERR) {
474 #ifndef ASC_RESETNEEDED
475 if(tp!=oldfn){
476 ERROR_REPORTER_HERE(ASC_PROG_WARNING,"Resetting signal %d (was=%p, new=%p",signum,oldfn,tp);
477 }
478 #endif
479 (void)signal(signum,tp);
480 }
481 }else{
482 (void)SIGNAL(signum,SIG_DFL);
483 }
484 }
485
486 /**
487 Append a pointer to the list given, if the list is not full.
488 */
489 static int push_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp){
490 if (tlist == NULL) {
491 ERROR_REPORTER_HERE(ASC_PROG_ERR,"TLIST is NULL");
492 return -1;
493 }
494 if (stackptr == NULL) {
495 ERROR_REPORTER_HERE(ASC_PROG_ERR,"STACKPTR is NULL");
496 return -1;
497 }
498 if (tp == NULL) {
499 ERROR_REPORTER_HERE(ASC_PROG_ERR,"TP is NULL");
500 return 2;
501 }
502 if (*stackptr > MAX_TRAP_DEPTH-1) {
503 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stackptr >= capacity");
504 return 1;
505 }
506 ++(*stackptr);
507 tlist[*stackptr] = tp;
508 return 0;
509 }
510
511
512 /**
513 @return 0 on success, 2 on NULL tlist or stackptr input, 1 on empty stack
514 or -1 on mismatched input tp and stack data
515
516 Any non-zero return code leaves the stack as it was.
517 */
518 static int pop_trap(SigHandlerFn **tlist, int *stackptr, SigHandlerFn *tp){
519 SigHandlerFn *oldtrap;
520
521 if ((tlist == NULL) || (stackptr == NULL)) {
522 return 2;
523 }
524 if (*stackptr < 0) {
525 return 1;
526 }
527 oldtrap = tlist[*stackptr];
528 if(oldtrap != tp)return -1;
529 tlist[*stackptr] = NULL;
530 --(*stackptr);
531 return 0;
532 }
533
534 static void print_stack(int signum, SigHandlerFn **tlist, int tos){
535 int i;
536 CONSOLE_DEBUG("---------------");
537 for(i=0;i<=tos;++i){
538 CONSOLE_DEBUG("Signal #%d, stack %d/%d: %p",signum,i,tos,tlist[i]);
539 }
540 CONSOLE_DEBUG("--------------- = %d",tos);
541 }
542
543 /*------------------------------------------
544 FPE ENV STACK
545 */
546
547 #ifdef HAVE_C99FPE
548
549 /**
550 Store current FPU state so that we can reset it later (after we've done
551 some stuff)
552
553 return 0 on success
554 */
555 static int fenv_push(fenv_t *stack, int *top, int excepts){
556 #ifdef SIGNAL_DEBUG
557 CONSOLE_DEBUG("Pushing FENV flags %d",excepts);
558 #endif
559
560 if(*top > MAX_TRAP_DEPTH - 1){
561 ERROR_REPORTER_HERE(ASC_PROG_ERR,"FPE stack is full");
562 return 1;
563 }
564 if(*top < -1){
565 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack top < -1");
566 return 2;
567 }
568 if(stack==NULL){
569 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack is NULL");
570 return 3;
571 }
572 fenv_t *fe = &stack[++(*top)];
573 if(fegetenv(fe)){
574 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to get env");
575 return 4;
576 }
577 fesetexceptflag(&g_fenv, excepts);
578 //CONSOLE_DEBUG("Enabled div-by-zero FPE exception (%d)",*top);
579 return 0;
580 }
581
582 /**
583 Restore a saved FPU state. Return 0 on success.
584 */
585 static int fenv_pop(fenv_t *stack, int *top){
586 #ifdef CONSOLE_DEBUG
587 CONSOLE_DEBUG("Popping FENV flags");
588 #endif
589 if(*top < 0){
590 ERROR_REPORTER_HERE(ASC_PROG_ERR,"FPE stack is empty");
591 return 1;
592 }
593 if(stack==NULL){
594 ERROR_REPORTER_HERE(ASC_PROG_ERR,"stack is NULL");
595 return 2;
596 }
597 fenv_t *fe = &stack[(*top)--];
598 if(fesetenv(fe)){
599 ERROR_REPORTER_HERE(ASC_PROG_ERR,"unable to set env");
600 return 3;
601 }
602 //CONSOLE_DEBUG("Restorted FPE state");
603 return 0;
604 }
605
606 #endif /* HAVE_C99FPE */
607

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