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