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 |
*//** |
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 |
|
53 |
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 |
Requires: |
99 |
#include "utilities/ascConfig.h" |
100 |
*//* |
101 |
by Benjamin Andrew Allan, May 27, 1997 |
102 |
Last in CVS: $Revision: 1.6 $ $Date: 1998/01/10 18:00:05 $ $Author: ballan $ |
103 |
*/ |
104 |
|
105 |
#ifndef ASC_ASCSIGNAL_H |
106 |
#define ASC_ASCSIGNAL_H |
107 |
|
108 |
#include "config.h" |
109 |
#ifndef ASC_SIGNAL_TRAPS |
110 |
|
111 |
/* if our wizzband PITA signal handing isn't turned on, at least allow |
112 |
use of feenableexcept(FE_EXCEPT_ALL) here and there. |
113 |
*/ |
114 |
# ifndef ASC_SIGNAL_TRAPS |
115 |
# ifdef HAVE_C99FPE |
116 |
# if __GNUC__ && !defined(_GNU_SOURCE) |
117 |
# define _GNU_SOURCE /* enables feenableexcept (http://gcc.gnu.org/ml/fortran/2005-10/msg00365.html) */ |
118 |
# endif |
119 |
# include <fenv.h> |
120 |
# endif |
121 |
# endif |
122 |
|
123 |
#else |
124 |
/*-------------- rest of file is conditional on ASC_SIGNAL_TRAPS--------------*/ |
125 |
|
126 |
#include "ascConfig.h" |
127 |
|
128 |
#include <general/except.h> |
129 |
|
130 |
#include <signal.h> |
131 |
|
132 |
#ifdef __WIN32__ |
133 |
# include <process.h> |
134 |
#else |
135 |
# include <unistd.h> |
136 |
#endif |
137 |
|
138 |
#ifdef __WIN32__ |
139 |
# define FPRESET _fpreset() |
140 |
#else |
141 |
# define FPRESET (void)0 |
142 |
#endif |
143 |
|
144 |
typedef void SigHandlerFn(int); |
145 |
/**< Signature of a signal handling function. */ |
146 |
|
147 |
#define MAX_TRAP_DEPTH 40L |
148 |
/**< The maximum number of traps that can be nested. */ |
149 |
|
150 |
ASC_DLLSPEC JMP_BUF g_fpe_env; /**< Standard signal jmp_buf - floating point error. */ |
151 |
ASC_DLLSPEC JMP_BUF g_seg_env; /**< Standard signal jmp_buf - segmentation fault. */ |
152 |
ASC_DLLSPEC JMP_BUF g_int_env; /**< Standard signal jmp_buf - interactive attention (<CTRL>C). */ |
153 |
|
154 |
#if 0 |
155 |
extern jmp_buf g_foreign_code_call_env; |
156 |
/**< |
157 |
Not currently in use. Should be when we get to a unified |
158 |
standard for signal handling. |
159 |
@todo Implement use of g_foreign_code_call_env? |
160 |
*/ |
161 |
#endif |
162 |
|
163 |
ASC_DLLSPEC void Asc_SignalTrap(int sigval); |
164 |
/**< |
165 |
* Standard signal handler. |
166 |
* This is the trap that should be used for most applications in |
167 |
* ASCEND. It prints a message then calls longjmp(GLOBAL, sigval) |
168 |
* where GLOBAL is one of g_fpe_env, g_seg_env, or g_int_env. |
169 |
* Because the jmp_buf is global, so you can't nest calls to |
170 |
* setjmp where both use this trap function.<br><br> |
171 |
* |
172 |
* Trivial Example: |
173 |
* <pre> |
174 |
* Asc_SignalHandlerPush(SIGFPE,Asc_SignalTrap); |
175 |
* if (setjmp(g_fpe_env)==0) { |
176 |
* y = sqrt(x); |
177 |
* } else { |
178 |
* y = sqrt(-x); |
179 |
* } |
180 |
* Asc_SignHandlerPop(SIGFPE,Asc_SignalTrap); |
181 |
* |
182 |
* For x < 0 the else is called because setjmp returns nonzero |
183 |
* when the body of the 'if' signals range error. |
184 |
* </pre> |
185 |
* Remember always to use Asc_SignalHandlerPush() and |
186 |
* Asc_SignalHandlerPop(). You can write an alternate function |
187 |
* to use instead of AscSignalTrap() if need be. The signals |
188 |
* SIGFPE, SIGINT, SIGSEGV are understood.<br><br> |
189 |
* |
190 |
* Note - this handler does not reinstall itself. After an exception, |
191 |
* you need to reinstall the handler (if desired) using |
192 |
* Asc_SignalRecover(). |
193 |
* |
194 |
* @todo Should utilities/ascSignal.c:Asc_SignalTrap() reinstall itself |
195 |
* after it catches an expection using Asc_SignalRecover()? |
196 |
* @param sigval Holds the signal type code when called during |
197 |
* an exception. |
198 |
*/ |
199 |
|
200 |
ASC_DLLSPEC int Asc_SignalInit(void); |
201 |
/**< |
202 |
* Initializes the signal manager. |
203 |
* This should be called before using any of the signal handling |
204 |
* functions in this module. It initializes the internal stacks |
205 |
* for mangaging signal handlers. This function does not install |
206 |
* any signal handlers (although any existing handlers are left |
207 |
* in place). Calling this function more than once will have no |
208 |
* effect and an error code will be returned.<br><br> |
209 |
* |
210 |
* @return Returns 0 if successful, 1 if memory could not be |
211 |
* allocated, and 2 if an error occurred. |
212 |
*/ |
213 |
|
214 |
ASC_DLLSPEC void Asc_SignalDestroy(void); |
215 |
/**< |
216 |
* Cleans up and destroys the stacks of signal handlers. |
217 |
* It does not change the status of any registered signal handlers |
218 |
* That is, any handlers registered when this function is called |
219 |
* will still be registered. It is important to call |
220 |
* Asc_SignalHandlerPop() for each occurrence of Asc_SignalHandlerPush() |
221 |
* before calling this function. Otherwise, any signal handlers |
222 |
* that were installed before Asc_SignalInit() was called will be lost. |
223 |
*/ |
224 |
|
225 |
ASC_DLLSPEC void Asc_SignalRecover(int force); |
226 |
/**< |
227 |
* Reinstalls the most recently pushed handler that has been |
228 |
* installed for each supported signal type. This should be called |
229 |
* after every trapped exception and at any other time when the |
230 |
* status of exception handlers may have become not well-defined. |
231 |
* If no handler has been pushed for a given signal type, SIG_DFL is |
232 |
* installed. Note that the standard handler function Asc_SignalTrap() |
233 |
* does not call this function. If you use the standard handler and |
234 |
* you want it reinstalled after an exception, be sure to call this |
235 |
* function after the longjmp return. This call is not particularly |
236 |
* cheap if it does the reinstallation.<br><br> |
237 |
* |
238 |
* This module tests on startup for whether the OS reverts to |
239 |
* SIG_DFL when a trap function is called. If it does NOT then |
240 |
* this function will simply return unless force != 0. You don't |
241 |
* want to call this function with force == 1 normally after a |
242 |
* caught exception. However, if you're not sure of the handler |
243 |
* installation status and want to make sure the handlers are |
244 |
* installed, call with force == 1. Also, gdb or other |
245 |
* debuggers which intercept and screw up signals may require |
246 |
* applying force (manually) to ensure that the signals get |
247 |
* reinstalled. |
248 |
* |
249 |
* @param force If non-zero, the most recent handlers are |
250 |
* reinstalled even if not required by the |
251 |
* compiler/platform. |
252 |
*/ |
253 |
|
254 |
ASC_DLLSPEC int Asc_SignalHandlerPushDefault(int signum); |
255 |
ASC_DLLSPEC int Asc_SignalHandlerPush(int signum, SigHandlerFn *func); |
256 |
/**< |
257 |
* Adds a handler to the stack of signal handlers for the given signal. |
258 |
* There is a maximum stack limit, so returns 1 if limit exceeded. |
259 |
* Returns -1 if stack of signal requested does not exist. |
260 |
* Pushing a NULL handler func does NOT change anything at all. |
261 |
* On a successful return, the handler has been installed and will |
262 |
* remain installed until a Asc_SignalHandlerPop() or another push. |
263 |
* The handler will remain installed as long as Asc_SignalRecover() |
264 |
* is used properly after every exception. |
265 |
* |
266 |
* @param signum The signal type that func should handle. |
267 |
* @param func The signal handler to register for signum signal types. |
268 |
* @return Returns 1 if the stack limit is exceeded, -1 if managing |
269 |
* of signals for the specified signum is not supported or |
270 |
* initialized, or 0 if the function completes successfully. |
271 |
* @todo Shouldn't utilities/ascSignal.c:Asc_SignalHandlerPush() return |
272 |
* an error code on a NULL func? It seems too easy for someone to |
273 |
* accidentally push a NULL without realizing it, and then later |
274 |
* popping an unintended handler. |
275 |
*/ |
276 |
|
277 |
ASC_DLLSPEC int Asc_SignalHandlerPopDefault(int signum); |
278 |
ASC_DLLSPEC int Asc_SignalHandlerPop(int signum, SigHandlerFn *func); |
279 |
/**< |
280 |
* Removes the last-pushed handler from the stack for signum signal types. |
281 |
* If the removed handler is the same as func, it is uninstalled and |
282 |
* replaced with the handler now at the top of the stack. If not, non-zero |
283 |
* is returned and you need to call Asc_SignalRecover() to uninstall the |
284 |
* current handler if desired. Note that the top handler is popped off |
285 |
* the stack whether it matches func or not. Non-zero is also returned if |
286 |
* the stack is empty. A side effect is that all managed signal types will |
287 |
* have the registered handlers reinstalled. |
288 |
* |
289 |
* @param signum The signal type whose top-most handler should be replaced. |
290 |
* @param func The handler function that should be at the top of the |
291 |
* stack (and currently installed) for signals of type signum. |
292 |
* @return Returns non-zero if func is not the replaced handler or if |
293 |
* the stack is empty, 0 if the function completed successfully. |
294 |
* @todo Does it make more sense for utilities/ascSignal.c:Asc_SignalHanderPop() |
295 |
* to fail completely if func is not the top-most handler? It is not |
296 |
* clear why the function should pop the top handler no matter what, but |
297 |
* only call Asc_SignalRecover() if it matches func. |
298 |
*/ |
299 |
|
300 |
/** Output the contents of the specified stack. For debugging. */ |
301 |
ASC_DLLSPEC void Asc_SignalPrintStack(int signum); |
302 |
|
303 |
/** Return the length of the specified stack. For debugging. */ |
304 |
ASC_DLLSPEC int Asc_SignalStackLength(int signum); |
305 |
|
306 |
/** For debugging. |
307 |
@return handler at top of specified stack, or NULL if stack is empty. |
308 |
*/ |
309 |
ASC_DLLSPEC SigHandlerFn *Asc_SignalStackTop(int signum); |
310 |
|
311 |
#endif /* ASC_SIGNAL_TRAPS */ |
312 |
|
313 |
#endif /* ASC_ASCSIGNAL_H */ |
314 |
|