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