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

Annotation of /trunk/ascend/utilities/ascSignal.h

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2535 - (hide annotations) (download) (as text)
Thu Jan 26 00:19:50 2012 UTC (10 years, 6 months ago) by jpye
File MIME type: text/x-chdr
File size: 13043 byte(s)
New implementation of signal handler stacks, less code.
Fixed one issue with utilities_ascSignal test cases, but can't work out reason for the other.
1 johnpye 526 /* ASCEND modelling environment
2     Copyright (C) 1997 Benjamin Andrew Allan
3     Copyright (C) 2006 Carnegie Mellon University
4 jds 54
5 johnpye 526 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     *//**
20     @file
21     Signal handling protocol definitions for ASCEND.
22    
23     This file standardizes the handling of signals because some OS
24     reset signals to SIG_DFL when a trap goes off while others
25     process the signal but leave the trapping function in place.
26     We want the second behavior and this gives it to us.
27    
28     This module implements limited support for managing signal handlers.
29     This includes:
30     - a standard signal handler - Asc_SignalTrap()
31     - global jmp_buf's for use with Asc_SignalTrap()
32     - functions for managing nested signal handlers
33    
34     The following signal types are currently supported:
35     - SIGFPE - floating point exception
36     - SIGINT - CTRL-C interactive attention request
37     - SIGSEGV - segmentation fault
38    
39     A simple use of these facilities to trap floating point exceptions
40     might be as follows:
41     <pre>
42     Asc_SignalInit();
43     Asc_SignalHandlerPush(SIGFPE, Asc_SignalTrap);
44     if (setjmp(g_fpe_env)==0) {
45     y = sqrt(x);
46     } else {
47     y = sqrt(-x);
48     }
49     Asc_SignHandlerPop(SIGFPE,Asc_SignalTrap);
50     Asc_SignalDestroy();
51     </pre>
52 jds 54
53 johnpye 968 This example uses the built-in signal handler Asc_SignalTrap()
54     and the global <code>jmp_buf</code> g_fpe_env. After initializing
55     the signal manager and registering the handler, <code>setjmp</code>
56     is used to select normal and exception paths. The <code>setjmp</code>
57     returns 0 when initially called and the sqrt(x) is calculated. If
58     x is negative, a SIGFPE exception occurs and the handler is called. It
59     uses <code>lngjmp</code> and returns to the if statement, and now
60     'setjmp' returns non-zero and the <code>else</code> clause is executed.
61     Finally, the handler is removed and the signal manager cleaned up.<br><br>
62    
63     The stack mechanism also allows nested handlers to be registered. It is
64     important to note that nested handlers for the same signal type cannot
65     both use Asc_SignalTrap() as the handler. This is because different
66     <code>jmp_buf</code> variables must be used and Asc_SignalTrap() uses
67     the same global <code>jmp_buf</code> each time. However, you can use
68     custome <code>jmp_buf</code>'s and handlers:
69    
70     <pre>
71     Asc_SignalInit();
72     Asc_SignalHandlerPush(SIGFPE, Asc_SignalTrap);
73     if (setjmp(g_fpe_env) == 0) {
74     y = sqrt(x);
75     Asc_SignalHandlerPush(SIGFPE, my_handler);
76     if (setjmp(my_jmp_buf) == 0) {
77     y = z/x;
78     } else {
79     Asc_Panic(1, NULL, "Div by zero error.");
80     }
81     Asc_SignHandlerPop(SIGFPE, my_handler);
82     } else {
83     y = sqrt(-x);
84     }
85     Asc_SignHandlerPop(SIGFPE,Asc_SignalTrap);
86     Asc_SignalDestroy();
87     </pre>
88    
89     Here, exceptions in the sqrt(x) calculation are handled by the standard
90     Asc_SignalTrap(), while the division is handled by my_handler.<br><br>
91    
92     Avoid mixing use of the signal manager with direct calls to signal().
93     Once Asc_SignalInit() has been called, use of signal() directly is likely
94     to be lost or to corrupt the managed handlers.<br><br>
95    
96     Another warning: setjmp is expensive if called inside a fast loop.
97    
98 johnpye 526 Requires:
99     #include "utilities/ascConfig.h"
100     *//*
101 johnpye 953 by Benjamin Andrew Allan, May 27, 1997
102     Last in CVS: $Revision: 1.6 $ $Date: 1998/01/10 18:00:05 $ $Author: ballan $
103 johnpye 526 */
104    
105 johnpye 67 #ifndef ASC_ASCSIGNAL_H
106     #define ASC_ASCSIGNAL_H
107 jds 54
108 johnpye 1228 #include "config.h"
109 jpye 2316
110     /** @addtogroup utilities_signal Utilities Signal Handling
111     @{
112     */
113    
114 johnpye 1228 #ifndef ASC_SIGNAL_TRAPS
115    
116     /* if our wizzband PITA signal handing isn't turned on, at least allow
117     use of feenableexcept(FE_EXCEPT_ALL) here and there.
118     */
119     # ifndef ASC_SIGNAL_TRAPS
120     # ifdef HAVE_C99FPE
121     # if __GNUC__ && !defined(_GNU_SOURCE)
122     # define _GNU_SOURCE /* enables feenableexcept (http://gcc.gnu.org/ml/fortran/2005-10/msg00365.html) */
123     # endif
124     # include <fenv.h>
125     # endif
126     # endif
127    
128     #else
129     /*-------------- rest of file is conditional on ASC_SIGNAL_TRAPS--------------*/
130    
131 jpye 2323 #include <ascend/general/platform.h>
132 johnpye 1228
133 jpye 2018 #include <ascend/general/except.h>
134 johnpye 669
135 johnpye 1228 #include <signal.h>
136    
137 aw0a 1 #ifdef __WIN32__
138 jds 102 # include <process.h>
139 jds 101 #else
140 jds 102 # include <unistd.h>
141 aw0a 1 #endif
142    
143 johnpye 968 #ifdef __WIN32__
144     # define FPRESET _fpreset()
145     #else
146     # define FPRESET (void)0
147     #endif
148    
149 johnpye 953 typedef void SigHandlerFn(int);
150 jds 59 /**< Signature of a signal handling function. */
151 jds 54
152 aw0a 1 #define MAX_TRAP_DEPTH 40L
153 jds 59 /**< The maximum number of traps that can be nested. */
154 johnpye 997
155 johnpye 1063 ASC_DLLSPEC JMP_BUF g_fpe_env; /**< Standard signal jmp_buf - floating point error. */
156     ASC_DLLSPEC JMP_BUF g_seg_env; /**< Standard signal jmp_buf - segmentation fault. */
157     ASC_DLLSPEC JMP_BUF g_int_env; /**< Standard signal jmp_buf - interactive attention (<CTRL>C). */
158 johnpye 997
159     #if 0
160 aw0a 1 extern jmp_buf g_foreign_code_call_env;
161 jds 54 /**<
162 johnpye 953 Not currently in use. Should be when we get to a unified
163     standard for signal handling.
164     @todo Implement use of g_foreign_code_call_env?
165     */
166 johnpye 997 #endif
167 aw0a 1
168 johnpye 1063 ASC_DLLSPEC void Asc_SignalTrap(int sigval);
169 jds 54 /**<
170 jds 59 * Standard signal handler.
171 jds 54 * This is the trap that should be used for most applications in
172 jds 59 * ASCEND. It prints a message then calls longjmp(GLOBAL, sigval)
173     * where GLOBAL is one of g_fpe_env, g_seg_env, or g_int_env.
174     * Because the jmp_buf is global, so you can't nest calls to
175     * setjmp where both use this trap function.<br><br>
176 aw0a 1 *
177 jds 54 * Trivial Example:
178     * <pre>
179     * Asc_SignalHandlerPush(SIGFPE,Asc_SignalTrap);
180     * if (setjmp(g_fpe_env)==0) {
181     * y = sqrt(x);
182     * } else {
183     * y = sqrt(-x);
184     * }
185     * Asc_SignHandlerPop(SIGFPE,Asc_SignalTrap);
186 jds 59 *
187 aw0a 1 * For x < 0 the else is called because setjmp returns nonzero
188     * when the body of the 'if' signals range error.
189 jds 54 * </pre>
190 johnpye 485 * Remember always to use Asc_SignalHandlerPush() and
191     * Asc_SignalHandlerPop(). You can write an alternate function
192 jds 59 * to use instead of AscSignalTrap() if need be. The signals
193 jds 54 * SIGFPE, SIGINT, SIGSEGV are understood.<br><br>
194 aw0a 1 *
195 jds 59 * Note - this handler does not reinstall itself. After an exception,
196     * you need to reinstall the handler (if desired) using
197     * Asc_SignalRecover().
198     *
199     * @todo Should utilities/ascSignal.c:Asc_SignalTrap() reinstall itself
200     * after it catches an expection using Asc_SignalRecover()?
201     * @param sigval Holds the signal type code when called during
202     * an exception.
203 aw0a 1 */
204    
205 johnpye 1063 ASC_DLLSPEC int Asc_SignalInit(void);
206 johnpye 485 /**<
207 jds 59 * Initializes the signal manager.
208 johnpye 485 * This should be called before using any of the signal handling
209 jds 59 * functions in this module. It initializes the internal stacks
210     * for mangaging signal handlers. This function does not install
211     * any signal handlers (although any existing handlers are left
212     * in place). Calling this function more than once will have no
213     * effect and an error code will be returned.<br><br>
214     *
215     * @return Returns 0 if successful, 1 if memory could not be
216     * allocated, and 2 if an error occurred.
217 aw0a 1 */
218    
219 johnpye 1063 ASC_DLLSPEC void Asc_SignalDestroy(void);
220 jds 59 /**<
221     * Cleans up and destroys the stacks of signal handlers.
222     * It does not change the status of any registered signal handlers
223     * That is, any handlers registered when this function is called
224     * will still be registered. It is important to call
225     * Asc_SignalHandlerPop() for each occurrence of Asc_SignalHandlerPush()
226     * before calling this function. Otherwise, any signal handlers
227     * that were installed before Asc_SignalInit() was called will be lost.
228 aw0a 1 */
229    
230 johnpye 1063 ASC_DLLSPEC void Asc_SignalRecover(int force);
231 jds 59 /**<
232     * Reinstalls the most recently pushed handler that has been
233     * installed for each supported signal type. This should be called
234     * after every trapped exception and at any other time when the
235     * status of exception handlers may have become not well-defined.
236     * If no handler has been pushed for a given signal type, SIG_DFL is
237 johnpye 485 * installed. Note that the standard handler function Asc_SignalTrap()
238     * does not call this function. If you use the standard handler and
239     * you want it reinstalled after an exception, be sure to call this
240     * function after the longjmp return. This call is not particularly
241 jds 59 * cheap if it does the reinstallation.<br><br>
242 aw0a 1 *
243 jds 59 * This module tests on startup for whether the OS reverts to
244     * SIG_DFL when a trap function is called. If it does NOT then
245     * this function will simply return unless force != 0. You don't
246     * want to call this function with force == 1 normally after a
247     * caught exception. However, if you're not sure of the handler
248 johnpye 485 * installation status and want to make sure the handlers are
249 jds 59 * installed, call with force == 1. Also, gdb or other
250     * debuggers which intercept and screw up signals may require
251     * applying force (manually) to ensure that the signals get
252     * reinstalled.
253     *
254 johnpye 485 * @param force If non-zero, the most recent handlers are
255 jds 59 * reinstalled even if not required by the
256     * compiler/platform.
257 aw0a 1 */
258    
259 jpye 2535 /* add source line/file and signal handler name to call */
260     #define Asc_SignalHandlerPushDefault(SIG) Asc_SignalHandlerPush_impl((SIG),Asc_SignalTrap,"Asc_SignalTrap",__FILE__,__LINE__)
261    
262     #define Asc_SignalHandlerPush(SIG,FUNC) Asc_SignalHandlerPush_impl((SIG),(FUNC),#FUNC,__FILE__,__LINE__)
263 jds 54 /**<
264 aw0a 1 * Adds a handler to the stack of signal handlers for the given signal.
265     * There is a maximum stack limit, so returns 1 if limit exceeded.
266     * Returns -1 if stack of signal requested does not exist.
267 jds 54 * Pushing a NULL handler func does NOT change anything at all.
268 aw0a 1 * On a successful return, the handler has been installed and will
269 jds 59 * remain installed until a Asc_SignalHandlerPop() or another push.
270     * The handler will remain installed as long as Asc_SignalRecover()
271     * is used properly after every exception.
272     *
273     * @param signum The signal type that func should handle.
274     * @param func The signal handler to register for signum signal types.
275     * @return Returns 1 if the stack limit is exceeded, -1 if managing
276     * of signals for the specified signum is not supported or
277     * initialized, or 0 if the function completes successfully.
278     * @todo Shouldn't utilities/ascSignal.c:Asc_SignalHandlerPush() return
279     * an error code on a NULL func? It seems too easy for someone to
280 johnpye 485 * accidentally push a NULL without realizing it, and then later
281 jds 59 * popping an unintended handler.
282 aw0a 1 */
283    
284 jpye 2535 ASC_DLLSPEC int Asc_SignalHandlerPush_impl(int signum, SigHandlerFn *func, char *name, char *file, int line);
285    
286     #define Asc_SignalHandlerPopDefault(SIG) Asc_SignalHandlerPop_impl((SIG),Asc_SignalTrap,"Asc_SignalTrap",__FILE__,__LINE__)
287    
288     #define Asc_SignalHandlerPop(SIG,FUNC) Asc_SignalHandlerPop_impl((SIG),(FUNC),#FUNC,__FILE__,__LINE__)
289 jds 54 /**<
290 jds 59 * Removes the last-pushed handler from the stack for signum signal types.
291 johnpye 485 * If the removed handler is the same as func, it is uninstalled and
292 jds 59 * replaced with the handler now at the top of the stack. If not, non-zero
293     * is returned and you need to call Asc_SignalRecover() to uninstall the
294 johnpye 485 * current handler if desired. Note that the top handler is popped off
295     * the stack whether it matches func or not. Non-zero is also returned if
296     * the stack is empty. A side effect is that all managed signal types will
297 jds 59 * have the registered handlers reinstalled.
298     *
299     * @param signum The signal type whose top-most handler should be replaced.
300     * @param func The handler function that should be at the top of the
301     * stack (and currently installed) for signals of type signum.
302     * @return Returns non-zero if func is not the replaced handler or if
303     * the stack is empty, 0 if the function completed successfully.
304     * @todo Does it make more sense for utilities/ascSignal.c:Asc_SignalHanderPop()
305 johnpye 485 * to fail completely if func is not the top-most handler? It is not
306     * clear why the function should pop the top handler no matter what, but
307 jds 59 * only call Asc_SignalRecover() if it matches func.
308 aw0a 1 */
309    
310 jpye 2535 ASC_DLLSPEC int Asc_SignalHandlerPop_impl(int signum, SigHandlerFn *func, char *name, char *file, int line);
311    
312 johnpye 1003 /** Output the contents of the specified stack. For debugging. */
313 johnpye 1063 ASC_DLLSPEC void Asc_SignalPrintStack(int signum);
314 johnpye 1002
315 johnpye 1003 /** Return the length of the specified stack. For debugging. */
316 johnpye 1063 ASC_DLLSPEC int Asc_SignalStackLength(int signum);
317 johnpye 1002
318 johnpye 1003 /** For debugging.
319     @return handler at top of specified stack, or NULL if stack is empty.
320     */
321 johnpye 1063 ASC_DLLSPEC SigHandlerFn *Asc_SignalStackTop(int signum);
322 johnpye 1003
323 johnpye 1142 #endif /* ASC_SIGNAL_TRAPS */
324    
325 jpye 2316 /* @} */
326    
327 johnpye 67 #endif /* ASC_ASCSIGNAL_H */
328 jds 54

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