d476a01a06fc7a5f71e54765f2e5dce7ab6fa131
[ghc-hetmet.git] / ghc / rts / Signals.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Signals.c,v 1.21 2001/08/14 13:40:09 sewardj 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     I_ i;
48
49     if (sig < nHandlers)
50       return;
51
52     if (handlers == NULL)
53       handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
54     else
55       handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
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    Low-level signal handler
70
71    Places the requested handler on a stack of pending handlers to be
72    started up at the next context switch.
73    -------------------------------------------------------------------------- */
74
75 static void
76 generic_handler(int sig)
77 {
78     sigset_t signals;
79
80     /* Can't call allocate from here.  Probably can't call malloc
81        either.  However, we have to schedule a new thread somehow.
82
83        It's probably ok to request a context switch and allow the
84        scheduler to  start the handler thread, but how do we
85        communicate this to the scheduler?
86
87        We need some kind of locking, but with low overhead (i.e. no
88        blocking signals every time around the scheduler).
89        
90        Signal Handlers are atomic (i.e. they can't be interrupted), and
91        we can make use of this.  We just need to make sure the
92        critical section of the scheduler can't be interrupted - the
93        only way to do this is to block signals.  However, we can lower
94        the overhead by only blocking signals when there are any
95        handlers to run, i.e. the set of pending handlers is
96        non-empty.
97     */
98        
99     /* We use a stack to store the pending signals.  We can't
100        dynamically grow this since we can't allocate any memory from
101        within a signal handler.
102
103        Hence unfortunately we have to bomb out if the buffer
104        overflows.  It might be acceptable to carry on in certain
105        circumstances, depending on the signal.  
106     */
107
108     *next_pending_handler++ = deRefStablePtr(stgCast(StgStablePtr,handlers[sig]));
109
110     /* stack full? */
111     if (next_pending_handler == &pending_handler_buf[N_PENDING_HANDLERS]) {
112       barf("too many pending signals");
113     }
114     
115     /* re-establish the signal handler, and carry on */
116     sigemptyset(&signals);
117     sigaddset(&signals, sig);
118     sigprocmask(SIG_UNBLOCK, &signals, NULL);
119
120     context_switch = 1;
121 }
122
123 /* -----------------------------------------------------------------------------
124    Blocking/Unblocking of the user signals
125    -------------------------------------------------------------------------- */
126
127 static sigset_t userSignals;
128 static sigset_t savedSignals;
129
130 void
131 initUserSignals(void)
132 {
133     sigemptyset(&userSignals);
134 }
135
136 void
137 blockUserSignals(void)
138 {
139     sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
140 }
141
142 void
143 unblockUserSignals(void)
144 {
145     sigprocmask(SIG_SETMASK, &savedSignals, NULL);
146 }
147
148
149 /* -----------------------------------------------------------------------------
150    Install a Haskell signal handler.
151    -------------------------------------------------------------------------- */
152
153 StgInt 
154 stg_sig_install(StgInt sig, StgInt spi, StgStablePtr handler, sigset_t *mask)
155 {
156     sigset_t signals;
157     struct sigaction action;
158     StgInt previous_spi;
159
160     /* Block the signal until we figure out what to do */
161     /* Count on this to fail if the signal number is invalid */
162     if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
163        sigprocmask(SIG_BLOCK, &signals, NULL))
164       return STG_SIG_ERR;
165
166     more_handlers(sig);
167
168     previous_spi = handlers[sig];
169
170     switch(spi) {
171     case STG_SIG_IGN:
172         handlers[sig] = STG_SIG_IGN;
173         sigdelset(&userSignals, sig);
174         action.sa_handler = SIG_IGN;
175         break;
176         
177     case STG_SIG_DFL:
178         handlers[sig] = STG_SIG_DFL;
179         sigdelset(&userSignals, sig);
180         action.sa_handler = SIG_DFL;
181         break;
182
183     case STG_SIG_HAN:
184         handlers[sig] = (I_)handler;
185         sigaddset(&userSignals, sig);
186         action.sa_handler = generic_handler;
187         break;
188
189     default:
190         barf("stg_sig_install: bad spi");
191     }
192
193     if (mask != 0)
194         action.sa_mask = *mask;
195     else
196         sigemptyset(&action.sa_mask);
197
198     action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
199
200     if (sigaction(sig, &action, NULL) || 
201         sigprocmask(SIG_UNBLOCK, &signals, NULL)) 
202     {
203       /* need to return an error code, so avoid a stable pointer leak
204        * by freeing the previous handler if there was one.
205        */        
206       if (previous_spi >= 0) {
207           freeStablePtr(stgCast(StgStablePtr,handlers[sig]));
208       }
209       return STG_SIG_ERR;
210     }
211
212     return previous_spi;
213 }
214
215 /* -----------------------------------------------------------------------------
216    Creating new threads for the pending signal handlers.
217    -------------------------------------------------------------------------- */
218
219 void
220 start_signal_handlers(void)
221 {
222   blockUserSignals();
223   
224   while (next_pending_handler != pending_handler_buf) {
225
226     next_pending_handler--;
227
228     scheduleThread(
229        createIOThread(RtsFlags.GcFlags.initialStkSize, 
230                       (StgClosure *) *next_pending_handler));
231   }
232
233   unblockUserSignals();
234 }
235
236 #else /* PAR */
237 StgInt 
238 stg_sig_install(StgInt sig, StgInt spi, StgStablePtr handler, sigset_t *mask)
239 {
240   /* don't fflush(stdout); WORKAROUND bug in Linux glibc */
241   barf("no signal handling support in a parallel implementation");
242 }
243
244 void
245 start_signal_handlers(void)
246 {
247 }
248 #endif
249
250 /* -----------------------------------------------------------------------------
251    SIGINT handler.
252
253    We like to shutdown nicely after receiving a SIGINT, write out the
254    stats, write profiling info, close open files and flush buffers etc.
255    -------------------------------------------------------------------------- */
256
257 #ifdef SMP
258 pthread_t startup_guy;
259 #endif
260
261 static void
262 shutdown_handler(int sig STG_UNUSED)
263 {
264 #ifdef SMP
265   /* if I'm a worker thread, send this signal to the guy who
266    * originally called startupHaskell().  Since we're handling
267    * the signal, it won't be a "send to all threads" type of signal
268    * (according to the POSIX threads spec).
269    */
270   if (pthread_self() != startup_guy) {
271     pthread_kill(startup_guy, sig);
272   } else
273 #endif
274
275   /* If we're already trying to interrupt the RTS, terminate with
276    * extreme prejudice.  So the first ^C tries to exit the program
277    * cleanly, and the second one just kills it.
278    */
279   if (interrupted) {
280       exit(EXIT_INTERRUPTED);
281   } else {
282       interruptStgRts();
283   }
284 }
285
286 /*
287  * The RTS installs a default signal handler for catching
288  * SIGINT, so that we can perform an orderly shutdown.
289  *
290  * Haskell code may install their own SIGINT handler, which is
291  * fine, provided they're so kind as to put back the old one
292  * when they de-install.
293  *
294  * In addition to handling SIGINT, the RTS also handles SIGFPE
295  * by ignoring it.  Apparently IEEE requires floating-point
296  * exceptions to be ignored by default, but alpha-dec-osf3
297  * doesn't seem to do so.
298  */
299 void
300 init_default_handlers()
301 {
302     struct sigaction action,oact;
303
304 #ifdef SMP
305     startup_guy = pthread_self();
306 #endif
307     action.sa_handler = shutdown_handler;
308     sigemptyset(&action.sa_mask);
309     action.sa_flags = 0;
310     if (sigaction(SIGINT, &action, &oact) != 0) {
311       /* Oh well, at least we tried. */
312       prog_belch("failed to install SIGINT handler");
313     }
314
315     siginterrupt(SIGINT, 1);
316
317     action.sa_handler = SIG_IGN;
318     sigemptyset(&action.sa_mask);
319     action.sa_flags = 0;
320     if (sigaction(SIGFPE, &action, &oact) != 0) {
321       /* Oh well, at least we tried. */
322       prog_belch("failed to install SIGFPE handler");
323     }
324 #ifdef alpha_TARGET_ARCH
325     ieee_set_fp_control(0);
326 #endif
327 }
328
329 #endif /*! mingw32_TARGET_OS */