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