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