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