2 % (c) The AQUA Project, Glasgow University, 1995
4 %************************************************************************
6 \section[Signals.lc]{Signal Handlers}
8 %************************************************************************
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.
17 Then, there are the user-specified signal handlers to cope with.
18 Since they're pretty rudimentary, they shouldn't actually cause as
24 /* Treat nexttep3 and sunos4 alike. CaS */
25 #if defined(nextstep3_TARGET_OS)
26 # define NON_POSIX_SOURCE
29 #if defined(sunos4_TARGET_OS)
30 /* The sigaction in SunOS 4.1.X does not grok SA_SIGINFO */
31 # define NON_POSIX_SOURCE
34 #if defined(freebsd_TARGET_OS)
35 # define NON_POSIX_SOURCE
38 #if defined(linux_TARGET_OS)
39 # define NON_POSIX_SOURCE
40 /* sigh, linux w/ glibc needs _BSD_SOURCE to get caddr_t... (ToDo) */
43 #if defined(osf3_TARGET_OS) || defined(osf1_TARGET_OS)
44 /* The include files for OSF1 do not normally define SA_SIGINFO */
45 # define _OSF_SOURCE 1
49 /* SIGVTALRM not avail w/ POSIX_SOURCE, but worse things happen without */
50 /* SIGH: triple SIGH (WDP 95/07) */
56 #if defined(HAVE_SYS_TYPES_H)
57 # include <sys/types.h>
60 /* This is useful with the particular set of header files on my NeXT.
63 #if defined(HAVE_SYS_SIGNAL_H)
64 # include <sys/signal.h>
67 #if defined(HAVE_SIGNAL_H)
71 #if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
72 /* to look *inside* sigcontext...
74 sigcontext has moved and been protected from the General Public,
75 in later versions (>2), the sigcontext decl is protected by
76 a __KERNEL__ #ifdef. As ever, we workaround by trying to
77 be version savvy - the version numbers are currently just a guess!
78 (ToDo: determine at what version no. the sigcontext move
81 # ifndef LINUX_VERSION_CODE
82 # include <linux/version.h>
84 # if (LINUX_VERSION_CODE < 0x020000)
85 # include <asm/signal.h>
87 # include <asm/sigcontext.h>
92 #if defined(HAVE_SIGINFO_H)
93 /* DEC OSF1 seems to need this explicitly. Maybe others do as well? */
97 #if defined(cygwin32_TARGET_OS)
103 %************************************************************************
105 \subsection{Stack-check by protected-memory-faulting}
107 %************************************************************************
109 If we are checking stack overflow by page faulting, then we need to be
110 able to install a @SIGSEGV@ handler, preferably one which can
111 determine where the fault occurred, so that we can satisfy ourselves
112 that it really was a stack overflow and not some random segmentation
116 #if STACK_CHECK_BY_PAGE_FAULT
117 /* NB: At the moment, this is always false on nextstep3. CaS. */
119 extern P_ stks_space; /* Where the stacks live, from SMstacks.lc */
122 SunOS 4.x is too old to have @SA_SIGINFO@ as a flag to @sigaction@, so
123 we use the older @signal@ call instead. This means that we also have
124 to set up the handler to expect a different collection of arguments.
128 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS) \
129 || defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS) \
130 || defined(aix_TARGET_OS)
133 segv_handler(int sig,
134 /* NB: all except first argument are "implementation defined" */
135 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS)
136 int code, struct sigcontext *scp, caddr_t addr)
137 # else /* linux || aix */
138 # if defined(aix_TARGET_OS)
139 int code, struct sigcontext *scp)
141 struct sigcontext_struct scp)
145 extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
147 # if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
148 unsigned long addr = scp.cr2;
149 /* Magic info from Tommy Thorn! */
151 # if defined(aix_TARGET_OS)
152 caddr_t addr = scp->sc_jmpbuf.jmp_context.o_vaddr;
153 /* Magic guess by andre */
155 if ( (char *)addr >= (char *)stks_space
156 && (char *)addr < (char *)(stks_space + RTSflags.GcFlags.stksSize))
160 fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
165 install_segv_handler(void)
167 #if freebsd_TARGET_OS
168 /* FreeBSD seems to generate SIGBUS for stack overflows */
169 if (signal(SIGBUS, segv_handler) == SIG_ERR)
171 if (signal(SIGSEGV, segv_handler) == SIG_ERR)
175 return ((int) signal(SIGSEGV, segv_handler) == SIG_ERR);
176 /* I think the "== SIG_ERR" is saying "there was no
177 handler for SIGSEGV before this one". WDP 95/12
182 # elif defined(irix6_TARGET_OS)
185 segv_handler(int sig, siginfo_t *sip, void *dummy)
189 fprintf(stderr, "Segmentation fault caught, address unknown\n");
191 if (sip->si_addr >= (void *) stks_space
192 && sip->si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
194 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
200 install_segv_handler(STG_NO_ARGS)
202 struct sigaction action;
204 action.sa_sigaction = segv_handler;
205 sigemptyset(&action.sa_mask);
206 action.sa_flags = SA_SIGINFO;
208 return sigaction(SIGSEGV, &action, NULL);
211 # elif defined(cygwin32_TARGET_OS)
214 The signal handlers in cygwin32 are only passed the signal
215 number, no sigcontext/siginfo is passed as event data..sigh. For
216 SIGSEGV, to get at the violating address, we need to use the Win32's
217 GetThreadContext() to get at the faulting address.
227 context.ContextFlags = CONTEXT_CONTROL;
228 hThread = GetCurrentThread(); /* cannot fail */
229 t = GetThreadContext(hThread,&context);
233 fprintf(stderr, "Segmentation fault caught, address unknown\n");
235 void *si_addr = context.Eip; /* magic */
236 if (si_addr >= (void *) stks_space
237 && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
240 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
246 install_segv_handler()
248 return (int) signal(SIGSEGV, segv_handler) == -1;
251 # else /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
253 # if defined(irix_TARGET_OS)
254 /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
255 # define si_addr _data._fault._addr
259 segv_handler(int sig, siginfo_t *sip)
260 /* NB: the second "siginfo_t" argument is not really standard */
264 fprintf(stderr, "Segmentation fault caught, address unknown\n");
266 if (sip->si_addr >= (caddr_t) stks_space
267 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
270 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
276 install_segv_handler(STG_NO_ARGS)
278 struct sigaction action;
280 action.sa_handler = segv_handler;
281 sigemptyset(&action.sa_mask);
282 action.sa_flags = SA_SIGINFO;
284 return sigaction(SIGSEGV, &action, NULL);
287 # endif /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
289 #endif /* STACK_CHECK_BY_PAGE_FAULT */
293 %************************************************************************
295 \subsection{Virtual-timer alarm (for profiling, etc.)}
297 %************************************************************************
299 The timer interrupt is somewhat simpler, and we could probably use
300 sigaction across the board, but since we have committed ourselves to
301 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
305 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
309 extern I_ delayTicks;
312 extern P_ CurrentTSO;
316 cygwin32 does not support VTALRM (sigh) - to do anything
317 sensible here we use the underlying Win32 calls.
320 # if defined(cygwin32_TARGET_OS)
321 /* windows.h already included */
323 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
331 vtalrm_handler(int sig)
335 For the parallel world, currentTSO is set if there is any work
336 on the current PE. In this case we DO want to context switch,
337 in case other PEs have sent us messages which must be processed.
340 # if defined(PROFILING) || defined(PAR)
341 static I_ csTicks = 0, pTicks = 0;
343 if (time_profiling) {
344 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
345 # if ! defined(PROFILING)
346 handle_tick_serial();
348 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
349 || RTSflags.ProfFlags.doHeapProfile)
350 handle_tick_serial();
352 handle_tick_noserial();
355 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
361 Handling a tick for threads blocked waiting for file
362 descriptor I/O or time.
364 This requires some care since virtual time alarm ticks
365 can occur when we are in the GC. If that is the case,
366 we just increment a delayed timer tick counter, but do
367 not check to see if any TSOs have been made runnable
368 as a result. (Do a bulk update of their status once
369 the GC has completed).
371 If the vtalrm does not occur within GC, we try to promote
372 any of the waiting threads to the runnable list (see awaitEvent)
377 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
379 else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
380 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
383 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
384 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
386 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
387 PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
388 SparkLimit[REQUIRED_POOL] / 2;
389 if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
390 PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
391 SparkLimit[ADVISORY_POOL] / 2;
392 sparksIgnored += SparkLimit[REQUIRED_POOL] / 2;
396 if (CurrentTSO != NULL ||
398 if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
400 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
401 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
402 /* ToDo: anything else for GRAN? WDP */
410 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
411 /* windows.h already included for the segv_handling above */
414 TIMECALLBACK *vtalrm_cback;
417 void (*tick_handle)(STG_NO_ARGS);
420 tick_handler(uID,uMsg,dwUser,dw1,dw2)
431 int install_vtalrm_handler()
434 vtalrm_cback = vtalrm_handler;
439 vtalrm_cback = tick_handler;
440 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
441 || RTSflags.ProfFlags.doHeapProfile)
442 tick_handle = handle_tick_serial;
444 tick_handle = handle_tick_noserial;
450 blockVtAlrmSignal(STG_NO_ARGS)
452 timeKillEvent(vtalrm_id);
456 unblockVtAlrmSignal(STG_NO_ARGS)
459 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
461 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
465 #elif defined(sunos4_TARGET_OS)
468 install_vtalrm_handler(void)
473 old = signal(SIGVTALRM, vtalrm_handler);
475 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
476 || RTSflags.ProfFlags.doHeapProfile)
477 old = signal(SIGVTALRM, handle_tick_serial);
479 old = signal(SIGVTALRM, handle_tick_noserial);
481 return ((int) old == SIG_ERR);
484 static int vtalrm_mask;
487 blockVtAlrmSignal(STG_NO_ARGS)
489 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
493 unblockVtAlrmSignal(STG_NO_ARGS)
495 (void) sigsetmask(vtalrm_mask);
498 # else /* Not SunOS 4 */
501 install_vtalrm_handler(STG_NO_ARGS)
503 struct sigaction action;
506 action.sa_handler = vtalrm_handler;
508 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
509 || RTSflags.ProfFlags.doHeapProfile)
510 action.sa_handler = handle_tick_serial;
512 action.sa_handler = handle_tick_noserial;
515 sigemptyset(&action.sa_mask);
518 return sigaction(SIGVTALRM, &action, NULL);
522 blockVtAlrmSignal(STG_NO_ARGS)
526 sigemptyset(&signals);
527 sigaddset(&signals, SIGVTALRM);
529 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
533 unblockVtAlrmSignal(STG_NO_ARGS)
537 sigemptyset(&signals);
538 sigaddset(&signals, SIGVTALRM);
540 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
543 # endif /* ! SunOS 4 */
545 #endif /* PROFILING || CONCURRENT (but not GRAN) */
549 Signal handling support for user-specified signal handlers. Since we
550 need stable pointers to do this properly, we just refuse to try in the
551 parallel world. Sorry.
555 #if defined(PAR) /* || defined(GRAN) */
558 blockUserSignals(void)
564 unblockUserSignals(void)
570 # ifdef _POSIX_SOURCE
571 sig_install(sig, spi, mask)
574 sig_install(sig, spi)
580 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
588 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
589 extern void freeStablePointer PROTO((I_));
590 extern jmp_buf restart_main;
592 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
593 static I_ nHandlers = 0; /* Size of handlers array */
596 more_handlers(I_ sig)
603 if (handlers == NULL)
604 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
606 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
608 if (handlers == NULL) {
610 fprintf(stderr, "VM exhausted (in more_handlers)\n");
613 for(i = nHandlers; i <= sig; i++)
614 /* Fill in the new slots with default actions */
615 handlers[i] = STG_SIG_DFL;
622 # ifdef _POSIX_SOURCE
625 generic_handler(int sig)
629 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
630 if (! initStacks(&StorageMgrInfo)) {
632 fprintf(stderr, "initStacks failed!\n");
635 TopClosure = deRefStablePointer(handlers[sig]);
636 sigemptyset(&signals);
637 sigaddset(&signals, sig);
638 sigprocmask(SIG_UNBLOCK, &signals, NULL);
639 longjmp(restart_main, sig);
642 static sigset_t userSignals;
643 static sigset_t savedSignals;
646 initUserSignals(void)
648 sigemptyset(&userSignals);
652 blockUserSignals(void)
654 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
658 unblockUserSignals(void)
660 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
665 sig_install(sig, spi, mask)
671 struct sigaction action;
674 /* Block the signal until we figure out what to do */
675 /* Count on this to fail if the signal number is invalid */
676 if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
677 sigprocmask(SIG_BLOCK, &signals, NULL))
682 previous_spi = handlers[sig];
686 handlers[sig] = STG_SIG_IGN;
687 sigdelset(&userSignals, sig);
688 action.sa_handler = SIG_IGN;
692 handlers[sig] = STG_SIG_DFL;
693 sigdelset(&userSignals, sig);
694 action.sa_handler = SIG_DFL;
698 sigaddset(&userSignals, sig);
699 action.sa_handler = generic_handler;
704 action.sa_mask = *mask;
706 sigemptyset(&action.sa_mask);
708 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
710 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
712 freeStablePointer(handlers[sig]);
724 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
725 if (! initStacks(&StorageMgrInfo)) {
727 fprintf(stderr, "initStacks failed!\n");
730 TopClosure = deRefStablePointer(handlers[sig]);
732 longjmp(restart_main, sig);
735 static int userSignals;
736 static int savedSignals;
739 initUserSignals(void)
745 blockUserSignals(void)
747 savedSignals = sigsetmask(userSignals);
751 unblockUserSignals(void)
753 sigsetmask(savedSignals);
757 sig_install(sig, spi)
763 void (*handler)(int);
765 /* Block the signal until we figure out what to do */
766 /* Count on this to fail if the signal number is invalid */
767 if(sig < 0 || (mask = sigmask(sig)) == 0)
770 mask = sigblock(mask);
774 previous_spi = handlers[sig];
778 handlers[sig] = STG_SIG_IGN;
779 userSignals &= ~sigmask(sig);
784 handlers[sig] = STG_SIG_DFL;
785 userSignals &= ~sigmask(sig);
790 userSignals |= sigmask(sig);
791 handler = generic_handler;
795 if (signal(sig, handler) < 0) {
797 freeStablePointer(handlers[sig]);