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