[project @ 1997-01-21 08:54:45 by sof]
[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 #if defined(cygwin32_TARGET_OS)
65 #include <windows.h>
66 #endif
67
68 \end{code}
69
70 %************************************************************************
71 %*                                                                      *
72 \subsection{Stack-check by protected-memory-faulting}
73 %*                                                                      *
74 %************************************************************************
75
76 If we are checking stack overflow by page faulting, then we need to be
77 able to install a @SIGSEGV@ handler, preferably one which can
78 determine where the fault occurred, so that we can satisfy ourselves
79 that it really was a stack overflow and not some random segmentation
80 fault.
81
82 \begin{code}
83 #if STACK_CHECK_BY_PAGE_FAULT
84
85 extern P_ stks_space;       /* Where the stacks live, from SMstacks.lc */
86 \end{code}
87
88 SunOS 4.x is too old to have @SA_SIGINFO@ as a flag to @sigaction@, so
89 we use the older @signal@ call instead.  This means that we also have
90 to set up the handler to expect a different collection of arguments.
91 Fun, eh?
92
93 \begin{code}
94 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS) \
95   || defined(linux_TARGET_OS)  || defined(linuxaout_TARGET_OS)
96
97 static void
98 segv_handler(int sig,
99     /* NB: all except first argument are "implementation defined" */
100 #  if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS)
101         int code, struct sigcontext *scp, caddr_t addr)
102 #  else /* linux */
103         struct sigcontext_struct scp)
104 #  endif /* linux */
105 {
106     extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
107
108 #  if defined(linux_TARGET_OS)  || defined(linuxaout_TARGET_OS)
109     caddr_t addr = scp.cr2;
110     /* Magic info from Tommy Thorn! */
111 #  endif
112
113     if (addr >= (caddr_t) stks_space
114       && addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
115         StackOverflow();
116
117     fflush(stdout);
118     fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
119     abort();
120 }
121
122 int
123 install_segv_handler(void)
124 {
125 #if freebsd_TARGET_OS
126     /* FreeBSD seems to generate SIGBUS for stack overflows */
127     if (signal(SIGBUS, segv_handler) == SIG_ERR)
128         return -1;
129     return ((int) signal(SIGSEGV, segv_handler));
130 #else
131     return ((int) signal(SIGSEGV, segv_handler) == SIG_ERR);
132     /* I think the "== SIG_ERR" is saying "there was no
133        handler for SIGSEGV before this one".  WDP 95/12
134     */
135 #endif
136 }
137
138 # else  /* Not SunOS 4, FreeBSD, or Linux(a.out) */
139
140 #  if defined(irix_TARGET_OS)
141      /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
142 #   define si_addr _data._fault._addr
143 #  endif
144
145 #if defined(cygwin32_TARGET_OS)
146 /*
147  The signal handlers in cygwin32 (beta14) are only passed the signal
148  number, no sigcontext/siginfo is passed as event data..sigh. For
149  SIGSEGV, to get at the violating address, we need to use the Win32's
150  WaitForDebugEvent() to get out any status information. 
151 */
152 static void
153 segv_handler(sig)
154  int sig;
155 {
156     /* From gdb/win32-nat.c */
157     DEBUG_EVENT event;
158     BOOL t = TRUE; /* WaitForDebugEvent (&event, INFINITE); */
159
160     fflush(stdout);
161     if (t == FALSE) {
162         fprintf(stderr, "Segmentation fault caught, address unknown\n");
163     } else {
164         void *si_addr = event.u.Exception.ExceptionRecord.ExceptionAddress;
165         if (si_addr >= (void *) stks_space
166           && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
167             StackOverflow();
168
169         fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
170     }
171     abort();
172 }
173
174 int
175 install_segv_handler()
176 {
177     return (int) signal(SIGSEGV, segv_handler) == -1;
178 }
179
180
181 #else /* !defined(cygwin32_TARGET_OS) */
182
183 static void
184 segv_handler(int sig, siginfo_t *sip)
185   /* NB: the second "siginfo_t" argument is not really standard */
186 {
187     fflush(stdout);
188     if (sip == NULL) {
189         fprintf(stderr, "Segmentation fault caught, address unknown\n");
190     } else {
191         if (sip->si_addr >= (caddr_t) stks_space
192           && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
193             StackOverflow();
194
195         fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
196     }
197     abort();
198 }
199
200 int
201 install_segv_handler(STG_NO_ARGS)
202 {
203     struct sigaction action;
204
205     action.sa_handler = segv_handler;
206     sigemptyset(&action.sa_mask);
207     action.sa_flags = SA_SIGINFO;
208
209     return sigaction(SIGSEGV, &action, NULL);
210 }
211
212 #endif /* not cygwin32_TARGET_OS */
213
214 # endif    /* not SunOS 4 */
215
216 #endif  /* STACK_CHECK_BY_PAGE_FAULT */
217
218 \end{code}
219
220 %************************************************************************
221 %*                                                                      *
222 \subsection{Virtual-timer alarm (for profiling, etc.)}
223 %*                                                                      *
224 %************************************************************************
225
226 The timer interrupt is somewhat simpler, and we could probably use
227 sigaction across the board, but since we have committed ourselves to
228 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
229 here.
230
231 \begin{code}
232 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
233
234 # ifdef CONCURRENT
235
236 extern I_ delayTicks;
237
238 #  ifdef PAR
239 extern P_ CurrentTSO;
240 #  endif
241
242 /*
243  cygwin32 does not support VTALRM (sigh) - to do anything
244  sensible here we use the underlying Win32 calls.
245  (will this work??)
246 */
247 #   if defined(cygwin32_TARGET_OS)
248 /* windows.h already included */
249 static VOID CALLBACK 
250 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
251 int uID;
252 unsigned int uMsg;
253 unsigned int dwUser;
254 unsigned int dw1;
255 unsigned int dw2;
256 #   else
257 static void
258 vtalrm_handler(int sig)
259 #   endif
260 {
261 /*
262    For the parallel world, currentTSO is set if there is any work
263    on the current PE.  In this case we DO want to context switch,
264    in case other PEs have sent us messages which must be processed.
265 */
266
267 #  if defined(PROFILING) || defined(PAR)
268     static I_ csTicks = 0, pTicks = 0;
269
270     if (time_profiling) {
271         if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
272 #   if ! defined(PROFILING)
273             handle_tick_serial();
274 #   else
275             if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
276              || RTSflags.ProfFlags.doHeapProfile)
277                 handle_tick_serial();
278             else
279                 handle_tick_noserial();
280 #   endif
281         }
282         if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
283             return;
284     }
285 #  endif
286
287        /*
288          Handling a tick for threads blocked waiting for file
289          descriptor I/O or time.
290
291          This requires some care since virtual time alarm ticks
292          can occur when we are in the GC. If that is the case,
293          we just increment a delayed timer tick counter, but do
294          not check to see if any TSOs have been made runnable
295          as a result. (Do a bulk update of their status once
296          the GC has completed).
297
298          If the vtalrm does not occur within GC, we try to promote
299          any of the waiting threads to the runnable list (see awaitEvent)
300
301          4/96 SOF
302        */
303
304     if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
305        delayTicks++;
306     else if (WaitingThreadsHd != Prelude_Z91Z93_closure)
307              AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
308
309 #  ifdef PAR
310     if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
311       PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
312         PruneSparks();
313         if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL]) 
314             PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
315               SparkLimit[REQUIRED_POOL] / 2;
316         if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
317             PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
318               SparkLimit[ADVISORY_POOL] / 2;
319             sparksIgnored += SparkLimit[REQUIRED_POOL] / 2; 
320         }
321     }
322
323     if (CurrentTSO != NULL ||
324 #  else
325     if (RunnableThreadsHd != Prelude_Z91Z93_closure ||
326 #  endif
327       PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
328       PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
329         /* ToDo: anything else for GRAN? WDP */
330         context_switch = 1;
331     }
332 }
333
334 # endif
335
336
337 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
338 /* windows.h already included for the segv_handling above */
339
340 I_ vtalrm_id;
341 TIMECALLBACK *vtalrm_cback;
342
343 #ifndef CONCURRENT
344 void (*tick_handle)(STG_NO_ARGS);
345
346 static VOID CALLBACK 
347 tick_handler(uID,uMsg,dwUser,dw1,dw2)
348 int uID;
349 unsigned int uMsg;
350 unsigned int dwUser;
351 unsigned int dw1;
352 unsigned int dw2;
353 {
354  (*tick_handle)();
355 }
356 #endif
357
358 int install_vtalrm_handler()
359 {
360 #  ifdef CONCURRENT
361     vtalrm_cback = vtalrm_handler;
362 #  else
363      /*
364         Only turn on ticking 
365      */
366     vtalrm_cback = tick_handler;
367     if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
368      || RTSflags.ProfFlags.doHeapProfile)
369         tick_handle = handle_tick_serial;
370     else
371         tick_handle = handle_tick_noserial;
372 #  endif
373     return (int)0;
374 }  
375
376 void
377 blockVtAlrmSignal(STG_NO_ARGS)
378 {
379  timeKillEvent(vtalrm_id);
380 }
381
382 void
383 unblockVtAlrmSignal(STG_NO_ARGS)
384 {
385 #ifdef CONCURRENT
386  timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
387 #else
388  timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
389 #endif
390 }
391
392 #elif defined(sunos4_TARGET_OS)
393
394 int
395 install_vtalrm_handler(void)
396 {
397     void (*old)();
398
399 #  ifdef CONCURRENT
400     old = signal(SIGVTALRM, vtalrm_handler);
401 #  else
402     if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
403      || RTSflags.ProfFlags.doHeapProfile)
404         old = signal(SIGVTALRM, handle_tick_serial);
405     else
406         old = signal(SIGVTALRM, handle_tick_noserial);
407 #  endif
408     return ((int) old == SIG_ERR);
409 }
410
411 static int vtalrm_mask;
412
413 void
414 blockVtAlrmSignal(STG_NO_ARGS)
415 {
416     vtalrm_mask = sigblock(sigmask(SIGVTALRM));
417 }
418
419 void
420 unblockVtAlrmSignal(STG_NO_ARGS)
421 {
422     (void) sigsetmask(vtalrm_mask);
423 }
424
425 # else  /* Not SunOS 4 */
426
427 int
428 install_vtalrm_handler(STG_NO_ARGS)
429 {
430     struct sigaction action;
431
432 #  ifdef CONCURRENT
433     action.sa_handler = vtalrm_handler;
434 #  else
435     if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
436      || RTSflags.ProfFlags.doHeapProfile)
437         action.sa_handler = handle_tick_serial;
438     else
439         action.sa_handler = handle_tick_noserial;
440 #  endif
441
442     sigemptyset(&action.sa_mask);
443     action.sa_flags = 0;
444
445     return sigaction(SIGVTALRM, &action, NULL);
446 }
447
448 void
449 blockVtAlrmSignal(STG_NO_ARGS)
450 {
451     sigset_t signals;
452     
453     sigemptyset(&signals);
454     sigaddset(&signals, SIGVTALRM);
455
456     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
457 }
458
459 void
460 unblockVtAlrmSignal(STG_NO_ARGS)
461 {
462     sigset_t signals;
463     
464     sigemptyset(&signals);
465     sigaddset(&signals, SIGVTALRM);
466
467     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
468 }
469
470 # endif /* ! SunOS 4 */
471
472 #endif /* PROFILING || CONCURRENT (but not GRAN) */
473
474 \end{code}
475
476 Signal handling support for user-specified signal handlers.  Since we
477 need stable pointers to do this properly, we just refuse to try in the
478 parallel world.  Sorry.
479
480 \begin{code}
481
482 #if defined(PAR) /* || defined(GRAN) */
483
484 void
485 blockUserSignals(void)
486 {
487     return;
488 }
489
490 void
491 unblockUserSignals(void)
492 {
493     return;
494 }
495
496 I_ 
497 # ifdef _POSIX_SOURCE
498 sig_install(sig, spi, mask)
499   sigset_t *mask;
500 # else
501   sig_install(sig, spi)
502 # endif
503   I_ sig;
504   I_ spi;
505 {
506     fflush(stdout);
507     fprintf(stderr,"No signal handling support in a parallel implementation.\n");
508     EXIT(EXIT_FAILURE);
509 }
510
511 #else   /* !PAR */
512
513 # include <setjmp.h>
514
515 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
516 extern void freeStablePointer PROTO((I_));
517 extern jmp_buf restart_main;
518
519 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
520 static I_ nHandlers = 0;    /* Size of handlers array */
521
522 static void
523 more_handlers(I_ sig)
524 {
525     I_ i;
526
527     if (sig < nHandlers)
528         return;
529
530     if (handlers == NULL)
531         handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
532     else
533         handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
534
535     if (handlers == NULL) {
536         fflush(stdout);
537         fprintf(stderr, "VM exhausted (in more_handlers)\n");
538         EXIT(EXIT_FAILURE);
539     }
540     for(i = nHandlers; i <= sig; i++)
541         /* Fill in the new slots with default actions */
542         handlers[i] = STG_SIG_DFL;
543
544     nHandlers = sig + 1;
545 }
546
547 I_ nocldstop = 0;
548
549 # ifdef _POSIX_SOURCE
550
551 static void
552 generic_handler(int sig)
553 {
554     sigset_t signals;
555
556     SAVE_Hp = SAVE_HpLim;       /* Just to be safe */
557     if (! initStacks(&StorageMgrInfo)) {
558         fflush(stdout);
559         fprintf(stderr, "initStacks failed!\n");
560         EXIT(EXIT_FAILURE);
561     }
562     TopClosure = deRefStablePointer(handlers[sig]);
563     sigemptyset(&signals);
564     sigaddset(&signals, sig);
565     sigprocmask(SIG_UNBLOCK, &signals, NULL);
566     longjmp(restart_main, sig);
567 }
568
569 static sigset_t userSignals;
570 static sigset_t savedSignals;
571
572 void
573 initUserSignals(void)
574 {
575     sigemptyset(&userSignals);
576 }
577
578 void
579 blockUserSignals(void)
580 {
581     sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
582 }
583
584 void
585 unblockUserSignals(void)
586 {
587     sigprocmask(SIG_SETMASK, &savedSignals, NULL);
588 }
589
590
591 I_ 
592 sig_install(sig, spi, mask)
593   I_ sig;
594   I_ spi;
595   sigset_t *mask;
596 {
597     sigset_t signals;
598     struct sigaction action;
599     I_ previous_spi;
600
601     /* Block the signal until we figure out what to do */
602     /* Count on this to fail if the signal number is invalid */
603     if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
604        sigprocmask(SIG_BLOCK, &signals, NULL))
605         return STG_SIG_ERR;
606
607     more_handlers(sig);
608
609     previous_spi = handlers[sig];
610
611     switch(spi) {
612     case STG_SIG_IGN:
613         handlers[sig] = STG_SIG_IGN;
614         sigdelset(&userSignals, sig);
615         action.sa_handler = SIG_IGN;
616         break;
617         
618     case STG_SIG_DFL:
619         handlers[sig] = STG_SIG_DFL;
620         sigdelset(&userSignals, sig);
621         action.sa_handler = SIG_DFL;
622         break;
623     default:
624         handlers[sig] = spi;
625         sigaddset(&userSignals, sig);
626         action.sa_handler = generic_handler;
627         break;
628     }
629
630     if (mask != NULL)
631         action.sa_mask = *mask;
632     else
633         sigemptyset(&action.sa_mask);
634
635     action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
636
637     if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
638         if (previous_spi)
639           freeStablePointer(handlers[sig]);
640         return STG_SIG_ERR;
641     }
642
643     return previous_spi;
644 }
645
646 # else  /* !POSIX */
647
648 static void
649 generic_handler(sig)
650 {
651     SAVE_Hp = SAVE_HpLim;       /* Just to be safe */
652     if (! initStacks(&StorageMgrInfo)) {
653         fflush(stdout);
654         fprintf(stderr, "initStacks failed!\n");
655         EXIT(EXIT_FAILURE);
656     }
657     TopClosure = deRefStablePointer(handlers[sig]);
658     sigsetmask(0);
659     longjmp(restart_main, sig);
660 }
661
662 static int userSignals;
663 static int savedSignals;
664
665 void
666 initUserSignals(void)
667 {
668     userSignals = 0;
669 }
670
671 void
672 blockUserSignals(void)
673 {
674     savedSignals = sigsetmask(userSignals);
675 }
676
677 void
678 unblockUserSignals(void)
679 {
680     sigsetmask(savedSignals);
681 }
682
683 I_ 
684 sig_install(sig, spi)
685   I_ sig;
686   I_ spi;
687 {
688     I_ previous_spi;
689     int mask;
690     void (*handler)(int);
691
692     /* Block the signal until we figure out what to do */
693     /* Count on this to fail if the signal number is invalid */
694     if(sig < 0 || (mask = sigmask(sig)) == 0)
695         return STG_SIG_ERR;
696
697     mask = sigblock(mask);
698
699     more_handlers(sig);
700
701     previous_spi = handlers[sig];
702
703     switch(spi) {
704     case STG_SIG_IGN:
705         handlers[sig] = STG_SIG_IGN;
706         userSignals &= ~sigmask(sig);
707         handler = SIG_IGN;
708         break;
709         
710     case STG_SIG_DFL:
711         handlers[sig] = STG_SIG_DFL;
712         userSignals &= ~sigmask(sig);
713         handler = SIG_DFL;
714         break;
715     default:
716         handlers[sig] = spi;
717         userSignals |= sigmask(sig);
718         handler = generic_handler;
719         break;
720     }
721
722     if (signal(sig, handler) < 0) {
723         if (previous_spi)
724           freeStablePointer(handlers[sig]);
725         sigsetmask(mask);
726         return STG_SIG_ERR;
727     }
728
729     sigsetmask(mask);
730     return previous_spi;
731 }
732
733 # endif    /* !POSIX */
734
735 #endif  /* PAR */
736
737 \end{code}