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(osf1_TARGET_OS)
39 /* The include files for OSF1 do not normally define SA_SIGINFO */
40 # define _OSF_SOURCE 1
44 /* SIGVTALRM not avail w/ POSIX_SOURCE, but worse things happen without */
45 /* SIGH: triple SIGH (WDP 95/07) */
51 #if defined(HAVE_SYS_TYPES_H)
52 # include <sys/types.h>
55 /* This is useful with the particular set of header files on my NeXT.
58 #if defined(HAVE_SYS_SIGNAL_H)
59 # include <sys/signal.h>
62 #if defined(HAVE_SIGNAL_H)
66 #if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
67 /* to look *inside* sigcontext...
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
76 # ifndef LINUX_VERSION_CODE
77 # include <linux/version.h>
79 # if (LINUX_VERSION_CODE < 0x020000)
80 # include <asm/signal.h>
82 # include <asm/sigcontext.h>
87 #if defined(HAVE_SIGINFO_H)
88 /* DEC OSF1 seems to need this explicitly. Maybe others do as well? */
92 #if defined(cygwin32_TARGET_OS)
98 %************************************************************************
100 \subsection{Stack-check by protected-memory-faulting}
102 %************************************************************************
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
111 #if STACK_CHECK_BY_PAGE_FAULT
112 /* NB: At the moment, this is always false on nextstep3. CaS. */
114 extern P_ stks_space; /* Where the stacks live, from SMstacks.lc */
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.
123 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS) \
124 || defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS) \
125 || defined(aix_TARGET_OS)
128 segv_handler(int sig,
129 /* NB: all except first argument are "implementation defined" */
130 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS)
131 int code, struct sigcontext *scp, caddr_t addr)
132 # else /* linux || aix */
133 # if defined(aix_TARGET_OS)
134 int code, struct sigcontext *scp)
136 struct sigcontext_struct scp)
140 extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
142 # if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
143 caddr_t addr = scp.cr2;
144 /* Magic info from Tommy Thorn! */
146 # if defined(aix_TARGET_OS)
147 caddr_t addr = scp->sc_jmpbuf.jmp_context.o_vaddr;
148 /* Magic guess by andre */
150 if (addr >= (caddr_t) stks_space
151 && addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
155 fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
160 install_segv_handler(void)
162 #if freebsd_TARGET_OS
163 /* FreeBSD seems to generate SIGBUS for stack overflows */
164 if (signal(SIGBUS, segv_handler) == SIG_ERR)
166 return ((int) signal(SIGSEGV, segv_handler));
168 return ((int) signal(SIGSEGV, segv_handler) == SIG_ERR);
169 /* I think the "== SIG_ERR" is saying "there was no
170 handler for SIGSEGV before this one". WDP 95/12
175 # elif defined(irix6_TARGET_OS)
178 segv_handler(int sig, siginfo_t *sip, void *dummy)
182 fprintf(stderr, "Segmentation fault caught, address unknown\n");
184 if (sip->si_addr >= (void *) stks_space
185 && sip->si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
187 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
193 install_segv_handler(STG_NO_ARGS)
195 struct sigaction action;
197 action.sa_sigaction = segv_handler;
198 sigemptyset(&action.sa_mask);
199 action.sa_flags = SA_SIGINFO;
201 return sigaction(SIGSEGV, &action, NULL);
204 # elif defined(cygwin32_TARGET_OS)
207 The signal handlers in cygwin32 (beta14) are only passed the signal
208 number, no sigcontext/siginfo is passed as event data..sigh. For
209 SIGSEGV, to get at the violating address, we need to use the Win32's
210 WaitForDebugEvent() to get out any status information.
216 /* From gdb/win32-nat.c */
218 BOOL t = WaitForDebugEvent (&event, INFINITE);
222 fprintf(stderr, "Segmentation fault caught, address unknown\n");
224 void *si_addr = event.u.Exception.ExceptionRecord.ExceptionAddress;
225 if (si_addr >= (void *) stks_space
226 && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
229 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
235 install_segv_handler()
237 return (int) signal(SIGSEGV, segv_handler) == -1;
241 # else /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
243 # if defined(irix_TARGET_OS)
244 /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
245 # define si_addr _data._fault._addr
249 segv_handler(int sig, siginfo_t *sip)
250 /* NB: the second "siginfo_t" argument is not really standard */
254 fprintf(stderr, "Segmentation fault caught, address unknown\n");
256 if (sip->si_addr >= (caddr_t) stks_space
257 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
260 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
266 install_segv_handler(STG_NO_ARGS)
268 struct sigaction action;
270 action.sa_handler = segv_handler;
271 sigemptyset(&action.sa_mask);
272 action.sa_flags = SA_SIGINFO;
274 return sigaction(SIGSEGV, &action, NULL);
277 # endif /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
279 #endif /* STACK_CHECK_BY_PAGE_FAULT */
283 %************************************************************************
285 \subsection{Virtual-timer alarm (for profiling, etc.)}
287 %************************************************************************
289 The timer interrupt is somewhat simpler, and we could probably use
290 sigaction across the board, but since we have committed ourselves to
291 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
295 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
299 extern I_ delayTicks;
302 extern P_ CurrentTSO;
306 cygwin32 does not support VTALRM (sigh) - to do anything
307 sensible here we use the underlying Win32 calls.
310 # if defined(cygwin32_TARGET_OS)
311 /* windows.h already included */
313 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
321 vtalrm_handler(int sig)
325 For the parallel world, currentTSO is set if there is any work
326 on the current PE. In this case we DO want to context switch,
327 in case other PEs have sent us messages which must be processed.
330 # if defined(PROFILING) || defined(PAR)
331 static I_ csTicks = 0, pTicks = 0;
333 if (time_profiling) {
334 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
335 # if ! defined(PROFILING)
336 handle_tick_serial();
338 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
339 || RTSflags.ProfFlags.doHeapProfile)
340 handle_tick_serial();
342 handle_tick_noserial();
345 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
351 Handling a tick for threads blocked waiting for file
352 descriptor I/O or time.
354 This requires some care since virtual time alarm ticks
355 can occur when we are in the GC. If that is the case,
356 we just increment a delayed timer tick counter, but do
357 not check to see if any TSOs have been made runnable
358 as a result. (Do a bulk update of their status once
359 the GC has completed).
361 If the vtalrm does not occur within GC, we try to promote
362 any of the waiting threads to the runnable list (see awaitEvent)
367 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
369 else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
370 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
373 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
374 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
376 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
377 PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
378 SparkLimit[REQUIRED_POOL] / 2;
379 if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
380 PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
381 SparkLimit[ADVISORY_POOL] / 2;
382 sparksIgnored += SparkLimit[REQUIRED_POOL] / 2;
386 if (CurrentTSO != NULL ||
388 if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
390 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
391 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
392 /* ToDo: anything else for GRAN? WDP */
400 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
401 /* windows.h already included for the segv_handling above */
404 TIMECALLBACK *vtalrm_cback;
407 void (*tick_handle)(STG_NO_ARGS);
410 tick_handler(uID,uMsg,dwUser,dw1,dw2)
421 int install_vtalrm_handler()
424 vtalrm_cback = vtalrm_handler;
429 vtalrm_cback = tick_handler;
430 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
431 || RTSflags.ProfFlags.doHeapProfile)
432 tick_handle = handle_tick_serial;
434 tick_handle = handle_tick_noserial;
440 blockVtAlrmSignal(STG_NO_ARGS)
442 timeKillEvent(vtalrm_id);
446 unblockVtAlrmSignal(STG_NO_ARGS)
449 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
451 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
455 #elif defined(sunos4_TARGET_OS)
458 install_vtalrm_handler(void)
463 old = signal(SIGVTALRM, vtalrm_handler);
465 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
466 || RTSflags.ProfFlags.doHeapProfile)
467 old = signal(SIGVTALRM, handle_tick_serial);
469 old = signal(SIGVTALRM, handle_tick_noserial);
471 return ((int) old == SIG_ERR);
474 static int vtalrm_mask;
477 blockVtAlrmSignal(STG_NO_ARGS)
479 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
483 unblockVtAlrmSignal(STG_NO_ARGS)
485 (void) sigsetmask(vtalrm_mask);
488 # else /* Not SunOS 4 */
491 install_vtalrm_handler(STG_NO_ARGS)
493 struct sigaction action;
496 action.sa_handler = vtalrm_handler;
498 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
499 || RTSflags.ProfFlags.doHeapProfile)
500 action.sa_handler = handle_tick_serial;
502 action.sa_handler = handle_tick_noserial;
505 sigemptyset(&action.sa_mask);
508 return sigaction(SIGVTALRM, &action, NULL);
512 blockVtAlrmSignal(STG_NO_ARGS)
516 sigemptyset(&signals);
517 sigaddset(&signals, SIGVTALRM);
519 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
523 unblockVtAlrmSignal(STG_NO_ARGS)
527 sigemptyset(&signals);
528 sigaddset(&signals, SIGVTALRM);
530 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
533 # endif /* ! SunOS 4 */
535 #endif /* PROFILING || CONCURRENT (but not GRAN) */
539 Signal handling support for user-specified signal handlers. Since we
540 need stable pointers to do this properly, we just refuse to try in the
541 parallel world. Sorry.
545 #if defined(PAR) /* || defined(GRAN) */
548 blockUserSignals(void)
554 unblockUserSignals(void)
560 # ifdef _POSIX_SOURCE
561 sig_install(sig, spi, mask)
564 sig_install(sig, spi)
570 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
578 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
579 extern void freeStablePointer PROTO((I_));
580 extern jmp_buf restart_main;
582 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
583 static I_ nHandlers = 0; /* Size of handlers array */
586 more_handlers(I_ sig)
593 if (handlers == NULL)
594 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
596 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
598 if (handlers == NULL) {
600 fprintf(stderr, "VM exhausted (in more_handlers)\n");
603 for(i = nHandlers; i <= sig; i++)
604 /* Fill in the new slots with default actions */
605 handlers[i] = STG_SIG_DFL;
612 # ifdef _POSIX_SOURCE
615 generic_handler(int sig)
619 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
620 if (! initStacks(&StorageMgrInfo)) {
622 fprintf(stderr, "initStacks failed!\n");
625 TopClosure = deRefStablePointer(handlers[sig]);
626 sigemptyset(&signals);
627 sigaddset(&signals, sig);
628 sigprocmask(SIG_UNBLOCK, &signals, NULL);
629 longjmp(restart_main, sig);
632 static sigset_t userSignals;
633 static sigset_t savedSignals;
636 initUserSignals(void)
638 sigemptyset(&userSignals);
642 blockUserSignals(void)
644 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
648 unblockUserSignals(void)
650 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
655 sig_install(sig, spi, mask)
661 struct sigaction action;
664 /* Block the signal until we figure out what to do */
665 /* Count on this to fail if the signal number is invalid */
666 if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
667 sigprocmask(SIG_BLOCK, &signals, NULL))
672 previous_spi = handlers[sig];
676 handlers[sig] = STG_SIG_IGN;
677 sigdelset(&userSignals, sig);
678 action.sa_handler = SIG_IGN;
682 handlers[sig] = STG_SIG_DFL;
683 sigdelset(&userSignals, sig);
684 action.sa_handler = SIG_DFL;
688 sigaddset(&userSignals, sig);
689 action.sa_handler = generic_handler;
694 action.sa_mask = *mask;
696 sigemptyset(&action.sa_mask);
698 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
700 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
702 freeStablePointer(handlers[sig]);
714 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
715 if (! initStacks(&StorageMgrInfo)) {
717 fprintf(stderr, "initStacks failed!\n");
720 TopClosure = deRefStablePointer(handlers[sig]);
722 longjmp(restart_main, sig);
725 static int userSignals;
726 static int savedSignals;
729 initUserSignals(void)
735 blockUserSignals(void)
737 savedSignals = sigsetmask(userSignals);
741 unblockUserSignals(void)
743 sigsetmask(savedSignals);
747 sig_install(sig, spi)
753 void (*handler)(int);
755 /* Block the signal until we figure out what to do */
756 /* Count on this to fail if the signal number is invalid */
757 if(sig < 0 || (mask = sigmask(sig)) == 0)
760 mask = sigblock(mask);
764 previous_spi = handlers[sig];
768 handlers[sig] = STG_SIG_IGN;
769 userSignals &= ~sigmask(sig);
774 handlers[sig] = STG_SIG_DFL;
775 userSignals &= ~sigmask(sig);
780 userSignals |= sigmask(sig);
781 handler = generic_handler;
785 if (signal(sig, handler) < 0) {
787 freeStablePointer(handlers[sig]);