[project @ 2001-10-31 10:34:29 by simonmar]
[ghc-hetmet.git] / ghc / rts / Signals.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Signals.c,v 1.22 2001/10/31 10:34:29 simonmar Exp $
3  *
4  * (c) The GHC Team, 1998-1999
5  *
6  * Signal processing / handling.
7  *
8  * ---------------------------------------------------------------------------*/
9
10 /* This is non-Posix-compliant.
11    #include "PosixSource.h" 
12 */
13 #include "Rts.h"
14 #include "SchedAPI.h"
15 #include "Schedule.h"
16 #include "Signals.h"
17 #include "RtsUtils.h"
18 #include "RtsFlags.h"
19 #include "StablePriv.h"
20
21 #ifdef alpha_TARGET_ARCH
22 #include <machine/fpu.h>
23 #endif
24
25 #ifndef mingw32_TARGET_OS
26
27 #ifndef PAR
28
29 /* SUP: The type of handlers is a little bit, well, doubtful... */
30 static StgInt *handlers = NULL; /* Dynamically grown array of signal handlers */
31 static StgInt nHandlers = 0;    /* Size of handlers array */
32
33 #define N_PENDING_HANDLERS 16
34
35 StgPtr pending_handler_buf[N_PENDING_HANDLERS];
36 StgPtr *next_pending_handler = pending_handler_buf;
37
38 StgInt nocldstop = 0;
39
40 /* -----------------------------------------------------------------------------
41  * Allocate/resize the table of signal handlers.
42  * -------------------------------------------------------------------------- */
43
44 static void
45 more_handlers(I_ sig)
46 {
47     StgInt i;
48
49     if (sig < nHandlers)
50         return;
51
52     if (handlers == NULL)
53         handlers = (StgInt *) malloc((sig + 1) * sizeof(StgInt));
54     else
55         handlers = (StgInt *) realloc(handlers, (sig + 1) * sizeof(StgInt));
56
57     if (handlers == NULL) {
58         // don't fflush(stdout); WORKAROUND bug in Linux glibc
59         barf("VM exhausted (in more_handlers)");
60     }
61     for(i = nHandlers; i <= sig; i++)
62         // Fill in the new slots with default actions
63         handlers[i] = STG_SIG_DFL;
64
65     nHandlers = sig + 1;
66 }
67
68 /* -----------------------------------------------------------------------------
69  * SIGCONT handler
70  *
71  * It seems that shells tend to put stdin back into blocking mode
72  * following a suspend/resume of the process.  Here we arrange to put
73  * it back into non-blocking mode.  We don't do anything to
74  * stdout/stderr because these handles don't get put into non-blocking
75  * mode at all - see the comments on stdout/stderr in PrelHandle.hsc.
76  * -------------------------------------------------------------------------- */
77
78 static void
79 cont_handler(int sig STG_UNUSED)
80 {
81     setNonBlockingFd(0);
82 }
83
84 /* -----------------------------------------------------------------------------
85  * Low-level signal handler
86  *
87  * Places the requested handler on a stack of pending handlers to be
88  * started up at the next context switch.
89  * -------------------------------------------------------------------------- */
90
91 static void
92 generic_handler(int sig)
93 {
94     sigset_t signals;
95
96     /* Can't call allocate from here.  Probably can't call malloc
97        either.  However, we have to schedule a new thread somehow.
98
99        It's probably ok to request a context switch and allow the
100        scheduler to  start the handler thread, but how do we
101        communicate this to the scheduler?
102
103        We need some kind of locking, but with low overhead (i.e. no
104        blocking signals every time around the scheduler).
105        
106        Signal Handlers are atomic (i.e. they can't be interrupted), and
107        we can make use of this.  We just need to make sure the
108        critical section of the scheduler can't be interrupted - the
109        only way to do this is to block signals.  However, we can lower
110        the overhead by only blocking signals when there are any
111        handlers to run, i.e. the set of pending handlers is
112        non-empty.
113     */
114        
115     /* We use a stack to store the pending signals.  We can't
116        dynamically grow this since we can't allocate any memory from
117        within a signal handler.
118
119        Hence unfortunately we have to bomb out if the buffer
120        overflows.  It might be acceptable to carry on in certain
121        circumstances, depending on the signal.  
122     */
123
124     *next_pending_handler++ = deRefStablePtr((StgStablePtr)handlers[sig]);
125
126     // stack full?
127     if (next_pending_handler == &pending_handler_buf[N_PENDING_HANDLERS]) {
128         barf("too many pending signals");
129     }
130     
131     // re-establish the signal handler, and carry on
132     sigemptyset(&signals);
133     sigaddset(&signals, sig);
134     sigprocmask(SIG_UNBLOCK, &signals, NULL);
135
136     // *always* do the SIGCONT handler, even if the user overrides it.
137     if (sig == SIGCONT) {
138         cont_handler(sig);
139     }
140
141     context_switch = 1;
142 }
143
144 /* -----------------------------------------------------------------------------
145  * Blocking/Unblocking of the user signals
146  * -------------------------------------------------------------------------- */
147
148 static sigset_t userSignals;
149 static sigset_t savedSignals;
150
151 void
152 initUserSignals(void)
153 {
154     sigemptyset(&userSignals);
155 }
156
157 void
158 blockUserSignals(void)
159 {
160     sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
161 }
162
163 void
164 unblockUserSignals(void)
165 {
166     sigprocmask(SIG_SETMASK, &savedSignals, NULL);
167 }
168
169
170 /* -----------------------------------------------------------------------------
171  * Install a Haskell signal handler.
172  * -------------------------------------------------------------------------- */
173
174 StgInt 
175 stg_sig_install(StgInt sig, StgInt spi, StgStablePtr handler, sigset_t *mask)
176 {
177     sigset_t signals;
178     struct sigaction action;
179     StgInt previous_spi;
180
181     // Block the signal until we figure out what to do
182     // Count on this to fail if the signal number is invalid
183     if (sig < 0 || sigemptyset(&signals) ||
184         sigaddset(&signals, sig) || sigprocmask(SIG_BLOCK, &signals, NULL)) {
185         return STG_SIG_ERR;
186     }
187     
188     more_handlers(sig);
189
190     previous_spi = handlers[sig];
191
192     switch(spi) {
193     case STG_SIG_IGN:
194         handlers[sig] = STG_SIG_IGN;
195         sigdelset(&userSignals, sig);
196         action.sa_handler = SIG_IGN;
197         break;
198         
199     case STG_SIG_DFL:
200         handlers[sig] = STG_SIG_DFL;
201         sigdelset(&userSignals, sig);
202         action.sa_handler = SIG_DFL;
203         break;
204
205     case STG_SIG_HAN:
206         handlers[sig] = (StgInt)handler;
207         sigaddset(&userSignals, sig);
208         action.sa_handler = generic_handler;
209         break;
210
211     default:
212         barf("stg_sig_install: bad spi");
213     }
214
215     if (mask != 0)
216         action.sa_mask = *mask;
217     else
218         sigemptyset(&action.sa_mask);
219
220     action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
221
222     if (sigaction(sig, &action, NULL) || 
223         sigprocmask(SIG_UNBLOCK, &signals, NULL)) 
224     {
225         // need to return an error code, so avoid a stable pointer leak
226         // by freeing the previous handler if there was one.
227         if (previous_spi >= 0) {
228             freeStablePtr(stgCast(StgStablePtr,handlers[sig]));
229         }
230         return STG_SIG_ERR;
231     }
232     
233     return previous_spi;
234 }
235
236 /* -----------------------------------------------------------------------------
237  * Creating new threads for the pending signal handlers.
238  * -------------------------------------------------------------------------- */
239 void
240 startSignalHandlers(void)
241 {
242   blockUserSignals();
243   
244   while (next_pending_handler != pending_handler_buf) {
245
246     next_pending_handler--;
247
248     scheduleThread(
249        createIOThread(RtsFlags.GcFlags.initialStkSize, 
250                       (StgClosure *) *next_pending_handler));
251   }
252
253   unblockUserSignals();
254 }
255
256 #else // PAR
257 StgInt 
258 stg_sig_install(StgInt sig, StgInt spi, StgStablePtr handler, sigset_t *mask)
259 {
260     // don't fflush(stdout); WORKAROUND bug in Linux glibc
261     barf("no signal handling support in a parallel implementation");
262 }
263
264 void
265 startSignalHandlers(void)
266 {
267 }
268 #endif
269
270 /* -----------------------------------------------------------------------------
271  * SIGINT handler.
272  *
273  * We like to shutdown nicely after receiving a SIGINT, write out the
274  * stats, write profiling info, close open files and flush buffers etc.
275  * -------------------------------------------------------------------------- */
276 #ifdef SMP
277 pthread_t startup_guy;
278 #endif
279
280 static void
281 shutdown_handler(int sig STG_UNUSED)
282 {
283 #ifdef SMP
284     // if I'm a worker thread, send this signal to the guy who
285     // originally called startupHaskell().  Since we're handling
286     // the signal, it won't be a "send to all threads" type of signal
287     // (according to the POSIX threads spec).
288     if (pthread_self() != startup_guy) {
289         pthread_kill(startup_guy, sig);
290         return;
291     }
292 #endif
293
294     // If we're already trying to interrupt the RTS, terminate with
295     // extreme prejudice.  So the first ^C tries to exit the program
296     // cleanly, and the second one just kills it.
297     if (interrupted) {
298         exit(EXIT_INTERRUPTED);
299     } else {
300         interruptStgRts();
301     }
302 }
303
304 /* -----------------------------------------------------------------------------
305  * Install default signal handlers.
306  *
307  * The RTS installs a default signal handler for catching
308  * SIGINT, so that we can perform an orderly shutdown.
309  *
310  * Haskell code may install their own SIGINT handler, which is
311  * fine, provided they're so kind as to put back the old one
312  * when they de-install.
313  *
314  * In addition to handling SIGINT, the RTS also handles SIGFPE
315  * by ignoring it.  Apparently IEEE requires floating-point
316  * exceptions to be ignored by default, but alpha-dec-osf3
317  * doesn't seem to do so.
318  * -------------------------------------------------------------------------- */
319 void
320 initDefaultHandlers()
321 {
322     struct sigaction action,oact;
323
324 #ifdef SMP
325     startup_guy = pthread_self();
326 #endif
327
328     // install the SIGINT handler
329     action.sa_handler = shutdown_handler;
330     sigemptyset(&action.sa_mask);
331     action.sa_flags = 0;
332     if (sigaction(SIGINT, &action, &oact) != 0) {
333         prog_belch("warning: failed to install SIGINT handler");
334     }
335
336     siginterrupt(SIGINT, 1);    // isn't this the default? --SDM
337
338     // install the SIGCONT handler
339     action.sa_handler = cont_handler;
340     sigemptyset(&action.sa_mask);
341     action.sa_flags = 0;
342     if (sigaction(SIGCONT, &action, &oact) != 0) {
343         prog_belch("warning: failed to install SIGCONT handler");
344     }
345
346     // install the SIGFPE handler
347     action.sa_handler = SIG_IGN;
348     sigemptyset(&action.sa_mask);
349     action.sa_flags = 0;
350     if (sigaction(SIGFPE, &action, &oact) != 0) {
351         prog_belch("warning: failed to install SIGFPE handler");
352     }
353 #ifdef alpha_TARGET_ARCH
354     ieee_set_fp_control(0);
355 #endif
356 }
357
358 #endif /*! mingw32_TARGET_OS */