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