1 |
/* |
2 |
* Signal handling protocol definitions for ASCEND |
3 |
* May 27, 1997 |
4 |
* By Benjamin Andrew Allan |
5 |
* Version: $Revision: 1.6 $ |
6 |
* Version control file: $RCSfile: ascSignal.h,v $ |
7 |
* Date last modified: $Date: 1998/01/10 18:00:05 $ |
8 |
* Last modified by: $Author: ballan $ |
9 |
* Part of Ascend |
10 |
* |
11 |
* This file is part of the Ascend Programming System. |
12 |
* This file standardizes the handling of signals because some OS |
13 |
* reset signals to SIG_DFL when a trap goes off while others |
14 |
* process the signal but leave the trapping function in place. |
15 |
* We want the second behavior and this gives it to us. |
16 |
* |
17 |
* Copyright (C) 1997 Benjamin Andrew Allan |
18 |
* |
19 |
* The Ascend Programming System is free software; you can redistribute |
20 |
* it and/or modify it under the terms of the GNU General Public License as |
21 |
* published by the Free Software Foundation; either version 2 of the |
22 |
* License, or (at your option) any later version. |
23 |
* |
24 |
* ASCEND is distributed in hope that it will be |
25 |
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of |
26 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
27 |
* General Public License for more details. |
28 |
* |
29 |
* You should have received a copy of the GNU General Public License |
30 |
* along with the program; if not, write to the Free Software Foundation, |
31 |
* Inc., 675 Mass Ave, Cambridge, MA 02139 USA. Check the file named |
32 |
* COPYING. |
33 |
*/ |
34 |
|
35 |
/** @file |
36 |
* Signal handling protocol definitions for ASCEND. |
37 |
* |
38 |
* This module implements limited support for managing signal handlers. |
39 |
* This includes: |
40 |
* - a standard signal handler - Asc_SignalTrap() |
41 |
* - global jmp_buf's for use with Asc_SignalTrap() |
42 |
* - functions for managing nested signal handlers |
43 |
* |
44 |
* The following signal types are currently supported: |
45 |
* - SIGFPE - floating point exception |
46 |
* - SIGINT - CTRL-C interactive attention request |
47 |
* - SIGSEGV - segmentation fault |
48 |
* |
49 |
* The signal handler manager uses gl_list_t lists, so the list |
50 |
* system must be initialized before calling Asc_SignalInit().<br><br> |
51 |
* |
52 |
* A simple use of these facilities to trap floating point exceptions |
53 |
* might be as follows: |
54 |
* <pre> |
55 |
* Asc_SignalInit(); |
56 |
* Asc_SignalHandlerPush(SIGFPE, Asc_SignalTrap); |
57 |
* if (setjmp(g_fpe_env)==0) { |
58 |
* y = sqrt(x); |
59 |
* } else { |
60 |
* y = sqrt(-x); |
61 |
* } |
62 |
* Asc_SignHandlerPop(SIGFPE,Asc_SignalTrap); |
63 |
* Asc_SignalDestroy(); |
64 |
* </pre> |
65 |
* This example uses the built-in signal handler Asc_SignalTrap() |
66 |
* and the global <code>jmp_buf</code> g_fpe_env. After initializing |
67 |
* the signal manager and registering the handler, <code>setjmp</code> |
68 |
* is used to select normal and exception paths. The <code>setjmp</code> |
69 |
* returns 0 when initially called and the sqrt(x) is calculated. If |
70 |
* x is negative, a SIGFPE exception occurs and the handler is called. It |
71 |
* uses <code>lngjmp</code> and returns to the if statement, and now |
72 |
* <setjmp> returns non-zero and the <code>else</code> clause is executed. |
73 |
* Finally, the handler is removed and the signal manager cleaned up.<br><br> |
74 |
* |
75 |
* The stack mechanism also allows nested handlers to be registered. It is |
76 |
* important to note that nested handlers for the same signal type cannot |
77 |
* both use Asc_SignalTrap() as the handler. This is because different |
78 |
* <code>jmp_buf</code> variables must be used and Asc_SignalTrap() uses |
79 |
* the same global <code>jmp_buf</code> each time. However, you can use |
80 |
* custome <code>jmp_buf</code>'s and handlers: |
81 |
* <pre> |
82 |
* Asc_SignalInit(); |
83 |
* Asc_SignalHandlerPush(SIGFPE, Asc_SignalTrap); |
84 |
* if (setjmp(g_fpe_env) == 0) { |
85 |
* y = sqrt(x); |
86 |
* Asc_SignalHandlerPush(SIGFPE, my_handler); |
87 |
* if (setjmp(my_jmp_buf) == 0) { |
88 |
* y = z/x; |
89 |
* } else { |
90 |
* Asc_Panic(1, NULL, "Div by zero error."); |
91 |
* } |
92 |
* Asc_SignHandlerPop(SIGFPE, my_handler); |
93 |
* } else { |
94 |
* y = sqrt(-x); |
95 |
* } |
96 |
* Asc_SignHandlerPop(SIGFPE,Asc_SignalTrap); |
97 |
* Asc_SignalDestroy(); |
98 |
* </pre> |
99 |
* Here, exceptions in the sqrt(x) calculation are handled by the standard |
100 |
* Asc_SignalTrap(), while the division is handled by my_handler.<br><br> |
101 |
* |
102 |
* Avoid mixing use of the signal manager with direct calls to signal(). |
103 |
* Once Asc_SignalInit() has been called, use of signal() directly is likely |
104 |
* to be lost or to corrupt the managed handlers.<br><br> |
105 |
* |
106 |
* Another warning: setjmp is expensive if called inside a fast loop. |
107 |
* <pre> |
108 |
* Requires: #include "utilities/ascConfig.h" |
109 |
* </pre> |
110 |
* @todo utilities/ascSignal stores function pointers in gl_lists as data |
111 |
* pointers (i.e. VOIDPTR). These are not guaranteed interchangable |
112 |
* and so arrays of function pointers or some other conforming |
113 |
* implementation should be used. |
114 |
*/ |
115 |
|
116 |
#ifndef ASC_ASCSIGNAL_H |
117 |
#define ASC_ASCSIGNAL_H |
118 |
|
119 |
#ifndef lint |
120 |
static CONST char ascSignalRCS[] = "$Id: ascSignal.h,v 1.6 1998/01/10 18:00:05 ballan Exp $"; |
121 |
#endif |
122 |
|
123 |
#include <signal.h> |
124 |
#include <setjmp.h> |
125 |
#include "utilities/ascConfig.h" |
126 |
#ifdef __WIN32__ |
127 |
#include <process.h> |
128 |
#else |
129 |
#include <unistd.h> |
130 |
#endif |
131 |
#include "general/list.h" |
132 |
|
133 |
typedef void (*SigHandler)(int); |
134 |
/**< Signature of a signal handling function. */ |
135 |
|
136 |
#define MAX_TRAP_DEPTH 40L |
137 |
/**< The maximum number of traps that can be nested. */ |
138 |
|
139 |
extern jmp_buf g_fpe_env; /**< Standard signal jmp_buf - floating point error. */ |
140 |
extern jmp_buf g_seg_env; /**< Standard signal jmp_buf - segmentation fault. */ |
141 |
extern jmp_buf g_int_env; /**< Standard signal jmp_buf - interactive attention (<CTRL>C). */ |
142 |
|
143 |
extern jmp_buf g_foreign_code_call_env; |
144 |
/**< |
145 |
* Not currently in use. Should be when we get to a unified |
146 |
* standard for signal handling. |
147 |
* @todo Implement use of g_foreign_code_call_env? |
148 |
*/ |
149 |
|
150 |
extern void Asc_SignalTrap(int sigval); |
151 |
/**< |
152 |
* Standard signal handler. |
153 |
* This is the trap that should be used for most applications in |
154 |
* ASCEND. It prints a message then calls longjmp(GLOBAL, sigval) |
155 |
* where GLOBAL is one of g_fpe_env, g_seg_env, or g_int_env. |
156 |
* Because the jmp_buf is global, so you can't nest calls to |
157 |
* setjmp where both use this trap function.<br><br> |
158 |
* |
159 |
* Trivial Example: |
160 |
* <pre> |
161 |
* Asc_SignalHandlerPush(SIGFPE,Asc_SignalTrap); |
162 |
* if (setjmp(g_fpe_env)==0) { |
163 |
* y = sqrt(x); |
164 |
* } else { |
165 |
* y = sqrt(-x); |
166 |
* } |
167 |
* Asc_SignHandlerPop(SIGFPE,Asc_SignalTrap); |
168 |
* |
169 |
* For x < 0 the else is called because setjmp returns nonzero |
170 |
* when the body of the 'if' signals range error. |
171 |
* </pre> |
172 |
* Remember always to use Asc_SignalHandlerPush() and |
173 |
* Asc_SignalHandlerPop(). You can write an alternate function |
174 |
* to use instead of AscSignalTrap() if need be. The signals |
175 |
* SIGFPE, SIGINT, SIGSEGV are understood.<br><br> |
176 |
* |
177 |
* Note - this handler does not reinstall itself. After an exception, |
178 |
* you need to reinstall the handler (if desired) using |
179 |
* Asc_SignalRecover(). |
180 |
* |
181 |
* @todo Should utilities/ascSignal.c:Asc_SignalTrap() reinstall itself |
182 |
* after it catches an expection using Asc_SignalRecover()? |
183 |
* @param sigval Holds the signal type code when called during |
184 |
* an exception. |
185 |
*/ |
186 |
|
187 |
extern int Asc_SignalInit(void); |
188 |
/**< |
189 |
* Initializes the signal manager. |
190 |
* This should be called before using any of the signal handling |
191 |
* functions in this module. It initializes the internal stacks |
192 |
* for mangaging signal handlers. This function does not install |
193 |
* any signal handlers (although any existing handlers are left |
194 |
* in place). Calling this function more than once will have no |
195 |
* effect and an error code will be returned.<br><br> |
196 |
* |
197 |
* The signal handler manager uses gl_list_t lists, so the list |
198 |
* system must be initialized before calling Asc_SignalInit(). |
199 |
* |
200 |
* @return Returns 0 if successful, 1 if memory could not be |
201 |
* allocated, and 2 if an error occurred. |
202 |
*/ |
203 |
|
204 |
extern void Asc_SignalDestroy(void); |
205 |
/**< |
206 |
* Cleans up and destroys the stacks of signal handlers. |
207 |
* It does not change the status of any registered signal handlers |
208 |
* That is, any handlers registered when this function is called |
209 |
* will still be registered. It is important to call |
210 |
* Asc_SignalHandlerPop() for each occurrence of Asc_SignalHandlerPush() |
211 |
* before calling this function. Otherwise, any signal handlers |
212 |
* that were installed before Asc_SignalInit() was called will be lost. |
213 |
*/ |
214 |
|
215 |
extern void Asc_SignalRecover(int force); |
216 |
/**< |
217 |
* Reinstalls the most recently pushed handler that has been |
218 |
* installed for each supported signal type. This should be called |
219 |
* after every trapped exception and at any other time when the |
220 |
* status of exception handlers may have become not well-defined. |
221 |
* If no handler has been pushed for a given signal type, SIG_DFL is |
222 |
* installed. Note that the standard handler function Asc_SignalTrap() |
223 |
* does not call this function. If you use the standard handler and |
224 |
* you want it reinstalled after an exception, be sure to call this |
225 |
* function after the longjmp return. This call is not particularly |
226 |
* cheap if it does the reinstallation.<br><br> |
227 |
* |
228 |
* This module tests on startup for whether the OS reverts to |
229 |
* SIG_DFL when a trap function is called. If it does NOT then |
230 |
* this function will simply return unless force != 0. You don't |
231 |
* want to call this function with force == 1 normally after a |
232 |
* caught exception. However, if you're not sure of the handler |
233 |
* installation status and want to make sure the handlers are |
234 |
* installed, call with force == 1. Also, gdb or other |
235 |
* debuggers which intercept and screw up signals may require |
236 |
* applying force (manually) to ensure that the signals get |
237 |
* reinstalled. |
238 |
* |
239 |
* @param force If non-zero, the most recent handlers are |
240 |
* reinstalled even if not required by the |
241 |
* compiler/platform. |
242 |
*/ |
243 |
|
244 |
extern int Asc_SignalHandlerPush(int signum, SigHandler func); |
245 |
/**< |
246 |
* Adds a handler to the stack of signal handlers for the given signal. |
247 |
* There is a maximum stack limit, so returns 1 if limit exceeded. |
248 |
* Returns -1 if stack of signal requested does not exist. |
249 |
* Pushing a NULL handler func does NOT change anything at all. |
250 |
* On a successful return, the handler has been installed and will |
251 |
* remain installed until a Asc_SignalHandlerPop() or another push. |
252 |
* The handler will remain installed as long as Asc_SignalRecover() |
253 |
* is used properly after every exception. |
254 |
* |
255 |
* @param signum The signal type that func should handle. |
256 |
* @param func The signal handler to register for signum signal types. |
257 |
* @return Returns 1 if the stack limit is exceeded, -1 if managing |
258 |
* of signals for the specified signum is not supported or |
259 |
* initialized, or 0 if the function completes successfully. |
260 |
* @todo Shouldn't utilities/ascSignal.c:Asc_SignalHandlerPush() return |
261 |
* an error code on a NULL func? It seems too easy for someone to |
262 |
* accidentally push a NULL without realizing it, and then later |
263 |
* popping an unintended handler. |
264 |
*/ |
265 |
|
266 |
extern int Asc_SignalHandlerPop(int signum, SigHandler func); |
267 |
/**< |
268 |
* Removes the last-pushed handler from the stack for signum signal types. |
269 |
* If the removed handler is the same as func, it is uninstalled and |
270 |
* replaced with the handler now at the top of the stack. If not, non-zero |
271 |
* is returned and you need to call Asc_SignalRecover() to uninstall the |
272 |
* current handler if desired. Note that the top handler is popped off |
273 |
* the stack whether it matches func or not. Non-zero is also returned if |
274 |
* the stack is empty. A side effect is that all managed signal types will |
275 |
* have the registered handlers reinstalled. |
276 |
* |
277 |
* @param signum The signal type whose top-most handler should be replaced. |
278 |
* @param func The handler function that should be at the top of the |
279 |
* stack (and currently installed) for signals of type signum. |
280 |
* @return Returns non-zero if func is not the replaced handler or if |
281 |
* the stack is empty, 0 if the function completed successfully. |
282 |
* @todo Does it make more sense for utilities/ascSignal.c:Asc_SignalHanderPop() |
283 |
* to fail completely if func is not the top-most handler? It is not |
284 |
* clear why the function should pop the top handler no matter what, but |
285 |
* only call Asc_SignalRecover() if it matches func. |
286 |
*/ |
287 |
|
288 |
#endif /* ASC_ASCSIGNAL_H */ |
289 |
|