[project @ 1996-01-08 20:28:12 by partain]
[ghc-hetmet.git] / ghc / runtime / main / Signals.lc
1 %
2 % (c) The AQUA Project, Glasgow University, 1995
3 %
4 %************************************************************************
5 %*                                                                      *
6 \section[Signals.lc]{Signal Handlers}
7 %*                                                                      *
8 %************************************************************************
9
10 There are two particular signals that we find interesting in the RTS:
11 segmentation faults (for cheap stack overflow checks) and virtual
12 timer alarms (for profiling and thread context switching).  POSIX
13 compliance is supposed to make this kind of thing easy, but it
14 doesn't.  Expect every new target platform to require gory hacks to
15 get this stuff to work.
16
17 Then, there are the user-specified signal handlers to cope with.
18 Since they're pretty rudimentary, they shouldn't actually cause as
19 much pain.
20
21 \begin{code}
22
23 #include "platform.h"
24
25 #if defined(sunos4_TARGET_OS)
26     /* The sigaction in SunOS 4.1.X does not grok SA_SIGINFO */
27 # define NON_POSIX_SOURCE
28 #endif
29
30 #if defined(osf1_TARGET_OS)
31     /* The include files for OSF1 do not normally define SA_SIGINFO */
32 # define _OSF_SOURCE 1
33 #endif
34
35 #if defined(linuxaout_TARGET_OS) || defined(linux_TARGET_OS)
36     /* I have no idea why this works (WDP 95/03) */
37 # define _BSD_SOURCE 1
38 #endif
39
40 #include "rtsdefs.h"
41
42 #if defined(HAVE_SYS_TYPES_H)
43 # include <sys/types.h>
44 #endif
45
46 #if defined(HAVE_SIGNAL_H)
47 # include <signal.h>
48 #endif
49 #if irix_TARGET_OS
50 /* SIGVTALRM not avail w/ POSIX_SOURCE, but worse things happen without */
51 /* SIGH: triple SIGH (WDP 95/07) */
52 # define SIGVTALRM 28
53 #endif
54
55 #if defined(HAVE_SIGINFO_H)
56     /* DEC OSF1 seems to need this explicitly.  Maybe others do as well? */
57 # include <siginfo.h>
58 #endif
59
60 \end{code}
61
62 %************************************************************************
63 %*                                                                      *
64 \subsection{Stack-check by protected-memory-faulting}
65 %*                                                                      *
66 %************************************************************************
67
68 If we are checking stack overflow by page faulting, then we need to be
69 able to install a @SIGSEGV@ handler, preferably one which can
70 determine where the fault occurred, so that we can satisfy ourselves
71 that it really was a stack overflow and not some random segmentation
72 fault.
73
74 \begin{code}
75
76 #if STACK_CHECK_BY_PAGE_FAULT
77
78 extern P_ stks_space;       /* Where the stacks live, from SMstacks.lc */
79 extern I_ SM_word_stk_size; /* How big they are (ditto) */
80
81 \end{code}
82
83 SunOS 4.x is too old to have @SA_SIGINFO@ as a flag to @sigaction@, so
84 we use the older @signal@ call instead.  This means that we also have
85 to set up the handler to expect a different collection of arguments.
86 Fun, eh?
87
88 \begin{code}
89
90 # if defined(sunos4_TARGET_OS) || defined(linuxaout_TARGET_OS) || defined(linux_TARGET_OS)
91
92 static void
93 segv_handler(sig, code, scp, addr)
94   int sig;
95   int code;
96   struct sigcontext *scp;
97   caddr_t addr;
98 {
99     extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
100
101     if (addr >= (caddr_t) stks_space
102       && addr < (caddr_t) (stks_space + SM_word_stk_size))
103         StackOverflow();
104
105     fflush(stdout);
106     fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
107     abort();
108 }
109
110 int
111 install_segv_handler()
112 {
113     return (int) signal(SIGSEGV, segv_handler) == -1;
114 }
115
116 # else  /* Not SunOS 4 */
117
118 #  if defined(irix_TARGET_OS)
119      /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
120 #   define si_addr _data._fault._addr
121 #  endif
122
123 static void
124 segv_handler(sig, sip)
125   int sig;
126   siginfo_t *sip;
127 {
128     fflush(stdout);
129     if (sip == NULL) {
130         fprintf(stderr, "Segmentation fault caught, address unknown\n");
131     } else {
132         if (sip->si_addr >= (caddr_t) stks_space
133           && sip->si_addr < (caddr_t) (stks_space + SM_word_stk_size))
134             StackOverflow();
135
136         fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
137     }
138     abort();
139 }
140
141 int
142 install_segv_handler()
143 {
144     struct sigaction action;
145
146     action.sa_handler = segv_handler;
147     sigemptyset(&action.sa_mask);
148     action.sa_flags = SA_SIGINFO;
149     return sigaction(SIGSEGV, &action, NULL);
150 }
151
152 # endif    /* not SunOS 4 */
153
154 #endif  /* STACK_CHECK_BY_PAGE_FAULT */
155
156 \end{code}
157
158 %************************************************************************
159 %*                                                                      *
160 \subsection{Virtual-timer alarm (for profiling, etc.)}
161 %*                                                                      *
162 %************************************************************************
163
164 The timer interrupt is somewhat simpler, and we could probably use
165 sigaction across the board, but since we have committed ourselves to
166 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
167 here.
168
169 \begin{code}
170 #if (defined(USE_COST_CENTRES) || defined(CONCURRENT)) && !defined(GRAN)
171
172 # if defined(USE_COST_CENTRES)
173 extern I_ heap_profiling_req;
174 # endif
175
176 # ifdef CONCURRENT
177
178 #  if defined(USE_COST_CENTRES) || defined(GUM)
179 I_ contextSwitchTicks;
180 I_ profilerTicks;
181 #  endif
182   
183 #  ifdef PAR
184 extern P_ CurrentTSO;
185 #  endif
186 extern I_ contextSwitchTime;
187
188 static void
189 vtalrm_handler(sig)
190   int sig;
191 {
192 /*
193    For the parallel world, currentTSO is set if there is any work
194    on the current PE.  In this case we DO want to context switch,
195    in case other PEs have sent us messages which must be processed.
196 */
197
198 #  if defined(USE_COST_CENTRES) || defined(GUM)
199     static I_ csTicks = 0, pTicks = 0;
200
201     if (time_profiling) {
202         if (++pTicks % profilerTicks == 0) {
203 #   if ! defined(USE_COST_CENTRES)
204             handle_tick_serial();
205 #   else
206             if (cc_profiling > 1 || heap_profiling_req != HEAP_NO_PROFILING)
207                 handle_tick_serial();
208             else
209                 handle_tick_noserial();
210 #   endif
211         }
212         if (++csTicks % contextSwitchTicks != 0)
213             return;
214     }
215 #  endif
216
217     if (WaitingThreadsHd != Nil_closure)
218         AwaitEvent(contextSwitchTime);
219
220 #  ifdef PAR
221     if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
222       PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
223         PruneSparks();
224         if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
225             PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
226               SparkLimit[REQUIRED_POOL] / 2;
227         if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL])
228             PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
229               SparkLimit[ADVISORY_POOL] / 2;
230     }
231
232     if (CurrentTSO != NULL ||
233 #  else
234     if (RunnableThreadsHd != Nil_closure ||
235 #  endif
236       PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
237       PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
238         /* ToDo: anything else for GRAN? WDP */
239         context_switch = 1;
240     }
241 }
242
243 # endif
244
245 # if defined(sunos4_TARGET_OS) || defined(linuxaout_TARGET_OS) || defined(linux_TARGET_OS)
246
247 int
248 install_vtalrm_handler()
249 {
250     void (*old)();
251
252 #  ifdef CONCURRENT
253     old = signal(SIGVTALRM, vtalrm_handler);
254 #  else
255     if (cc_profiling > 1 || heap_profiling_req != HEAP_NO_PROFILING)
256         old = signal(SIGVTALRM, handle_tick_serial);
257     else
258         old = signal(SIGVTALRM, handle_tick_noserial);
259 #  endif
260     return (int) old == -1;
261 }
262
263 static int vtalrm_mask;
264
265 void
266 blockVtAlrmSignal(STG_NO_ARGS)
267 {
268     vtalrm_mask = sigblock(sigmask(SIGVTALRM));
269 }
270
271 void
272 unblockVtAlrmSignal(STG_NO_ARGS)
273 {
274     (void) sigsetmask(vtalrm_mask);
275 }
276
277 # else  /* Not SunOS 4 */
278
279 int
280 install_vtalrm_handler(STG_NO_ARGS)
281 {
282     struct sigaction action;
283
284 #  ifdef CONCURRENT
285     action.sa_handler = vtalrm_handler;
286 #  else
287     if (cc_profiling > 1 || heap_profiling_req != HEAP_NO_PROFILING)
288         action.sa_handler = handle_tick_serial;
289     else
290         action.sa_handler = handle_tick_noserial;
291 #  endif
292
293     sigemptyset(&action.sa_mask);
294     action.sa_flags = 0;
295
296     return sigaction(SIGVTALRM, &action, NULL);
297 }
298
299 void
300 blockVtAlrmSignal(STG_NO_ARGS)
301 {
302     sigset_t signals;
303     
304     sigemptyset(&signals);
305     sigaddset(&signals, SIGVTALRM);
306
307     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
308 }
309
310 void
311 unblockVtAlrmSignal(STG_NO_ARGS)
312 {
313     sigset_t signals;
314     
315     sigemptyset(&signals);
316     sigaddset(&signals, SIGVTALRM);
317
318     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
319 }
320
321 # endif /* SunOS 4 */
322
323 #endif /* USE_COST_CENTRES || CONCURRENT (but not GRAN) */
324
325 \end{code}
326
327 Signal handling support for user-specified signal handlers.  Since we
328 need stable pointers to do this properly, we just refuse to try in the
329 parallel world.  Sorry.
330
331 \begin{code}
332
333 #ifdef PAR
334
335 void
336 blockUserSignals()
337 {
338     return;
339 }
340
341 void
342 unblockUserSignals()
343 {
344     return;
345 }
346
347 I_ 
348 # ifdef _POSIX_SOURCE
349 sig_install(sig, spi, mask)
350   sigset_t *mask;
351 # else
352   sig_install(sig, spi)
353 # endif
354   I_ sig;
355   I_ spi;
356 {
357     fflush(stdout);
358     fprintf(stderr,"No signal handling support in a parallel implementation.\n");
359     EXIT(EXIT_FAILURE);
360 }
361
362 #else   /* !PAR */
363
364 # include <setjmp.h>
365
366 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
367 extern void freeStablePointer PROTO((I_));
368 extern jmp_buf restart_main;
369
370 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
371 static I_ nHandlers = 0;    /* Size of handlers array */
372
373 static void
374 more_handlers(sig)
375   I_ sig;
376 {
377     I_ i;
378
379     if (sig < nHandlers)
380         return;
381
382     if (handlers == NULL)
383         handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
384     else
385         handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
386
387     if (handlers == NULL) {
388         fflush(stdout);
389         fprintf(stderr, "VM exhausted\n");
390         EXIT(EXIT_FAILURE);
391     }
392     for(i = nHandlers; i <= sig; i++)
393         /* Fill in the new slots with default actions */
394         handlers[i] = STG_SIG_DFL;
395
396     nHandlers = sig + 1;
397 }
398
399 # ifdef _POSIX_SOURCE
400
401 static void
402 generic_handler(sig)
403 {
404     sigset_t signals;
405
406     SAVE_Hp = SAVE_HpLim;       /* Just to be safe */
407     if (initStacks(&StorageMgrInfo) != 0) {
408         fflush(stdout);
409         fprintf(stderr, "initStacks failed!\n");
410         EXIT(EXIT_FAILURE);
411     }
412     TopClosure = deRefStablePointer(handlers[sig]);
413     sigemptyset(&signals);
414     sigaddset(&signals, sig);
415     sigprocmask(SIG_UNBLOCK, &signals, NULL);
416     longjmp(restart_main, sig);
417 }
418
419 static sigset_t userSignals;
420 static sigset_t savedSignals;
421
422 void
423 initUserSignals()
424 {
425     sigemptyset(&userSignals);
426 }
427
428 void
429 blockUserSignals()
430 {
431     sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
432 }
433
434 void
435 unblockUserSignals()
436 {
437     sigprocmask(SIG_SETMASK, &savedSignals, NULL);
438 }
439
440
441 I_ nocldstop = 0;
442
443 I_ 
444 sig_install(sig, spi, mask)
445   I_ sig;
446   I_ spi;
447   sigset_t *mask;
448 {
449     sigset_t signals;
450     struct sigaction action;
451     I_ previous_spi;
452
453     /* Block the signal until we figure out what to do */
454     /* Count on this to fail if the signal number is invalid */
455     if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
456        sigprocmask(SIG_BLOCK, &signals, NULL))
457         return STG_SIG_ERR;
458
459     more_handlers(sig);
460
461     previous_spi = handlers[sig];
462
463     switch(spi) {
464     case STG_SIG_IGN:
465         handlers[sig] = STG_SIG_IGN;
466         sigdelset(&userSignals, sig);
467         action.sa_handler = SIG_IGN;
468         break;
469         
470     case STG_SIG_DFL:
471         handlers[sig] = STG_SIG_DFL;
472         sigdelset(&userSignals, sig);
473         action.sa_handler = SIG_DFL;
474         break;
475     default:
476         handlers[sig] = spi;
477         sigaddset(&userSignals, sig);
478         action.sa_handler = generic_handler;
479         break;
480     }
481
482     if (mask != NULL)
483         action.sa_mask = *mask;
484     else
485         sigemptyset(&action.sa_mask);
486
487     action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
488     if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
489         if (previous_spi)
490           freeStablePointer(handlers[sig]);
491         return STG_SIG_ERR;
492     }
493
494     return previous_spi;
495 }
496
497 # else  /* !POSIX */
498
499 static void
500 generic_handler(sig)
501 {
502     SAVE_Hp = SAVE_HpLim;       /* Just to be safe */
503     if (initStacks(&StorageMgrInfo) != 0) {
504         fflush(stdout);
505         fprintf(stderr, "initStacks failed!\n");
506         EXIT(EXIT_FAILURE);
507     }
508     TopClosure = deRefStablePointer(handlers[sig]);
509     sigsetmask(0);
510     longjmp(restart_main, sig);
511 }
512
513 static int userSignals;
514 static int savedSignals;
515
516 void
517 initUserSignals()
518 {
519     userSignals = 0;
520 }
521
522 void
523 blockUserSignals()
524 {
525     savedSignals = sigsetmask(userSignals);
526 }
527
528 void
529 unblockUserSignals()
530 {
531     sigsetmask(savedSignals);
532 }
533
534 I_ 
535 sig_install(sig, spi)
536   I_ sig;
537   I_ spi;
538 {
539     I_ previous_spi;
540     int mask;
541     void (*handler)();
542
543     /* Block the signal until we figure out what to do */
544     /* Count on this to fail if the signal number is invalid */
545     if(sig < 0 || (mask = sigmask(sig)) == 0)
546         return STG_SIG_ERR;
547
548     mask = sigblock(mask);
549
550     more_handlers(sig);
551
552     previous_spi = handlers[sig];
553
554     switch(spi) {
555     case STG_SIG_IGN:
556         handlers[sig] = STG_SIG_IGN;
557         userSignals &= ~sigmask(sig);
558         handler = SIG_IGN;
559         break;
560         
561     case STG_SIG_DFL:
562         handlers[sig] = STG_SIG_DFL;
563         userSignals &= ~sigmask(sig);
564         handler = SIG_DFL;
565         break;
566     default:
567         handlers[sig] = spi;
568         userSignals |= sigmask(sig);
569         handler = generic_handler;
570         break;
571     }
572
573     if (signal(sig, handler) < 0) {
574         if (previous_spi)
575           freeStablePointer(handlers[sig]);
576         sigsetmask(mask);
577         return STG_SIG_ERR;
578     }
579
580     sigsetmask(mask);
581     return previous_spi;
582 }
583
584 # endif    /* POSIX */
585
586 #endif  /* PAR */
587
588 \end{code}