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) || defined(aix_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 # else /* Not SunOS 4, FreeBSD, or Linux(a.out) */
177 # if defined(irix_TARGET_OS)
178 /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
179 # define si_addr _data._fault._addr
182 #if defined(cygwin32_TARGET_OS)
184 The signal handlers in cygwin32 (beta14) are only passed the signal
185 number, no sigcontext/siginfo is passed as event data..sigh. For
186 SIGSEGV, to get at the violating address, we need to use the Win32's
187 WaitForDebugEvent() to get out any status information.
193 /* From gdb/win32-nat.c */
195 BOOL t = WaitForDebugEvent (&event, INFINITE);
199 fprintf(stderr, "Segmentation fault caught, address unknown\n");
201 void *si_addr = event.u.Exception.ExceptionRecord.ExceptionAddress;
202 if (si_addr >= (void *) stks_space
203 && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
206 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
212 install_segv_handler()
214 return (int) signal(SIGSEGV, segv_handler) == -1;
218 #else /* !defined(cygwin32_TARGET_OS) */
221 segv_handler(int sig, siginfo_t *sip)
222 /* NB: the second "siginfo_t" argument is not really standard */
226 fprintf(stderr, "Segmentation fault caught, address unknown\n");
228 if (sip->si_addr >= (caddr_t) stks_space
229 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
232 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
238 install_segv_handler(STG_NO_ARGS)
240 struct sigaction action;
242 action.sa_handler = segv_handler;
243 sigemptyset(&action.sa_mask);
244 action.sa_flags = SA_SIGINFO;
246 return sigaction(SIGSEGV, &action, NULL);
249 #endif /* not cygwin32_TARGET_OS */
251 # endif /* not SunOS 4 */
253 #endif /* STACK_CHECK_BY_PAGE_FAULT */
257 %************************************************************************
259 \subsection{Virtual-timer alarm (for profiling, etc.)}
261 %************************************************************************
263 The timer interrupt is somewhat simpler, and we could probably use
264 sigaction across the board, but since we have committed ourselves to
265 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
269 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
273 extern I_ delayTicks;
276 extern P_ CurrentTSO;
280 cygwin32 does not support VTALRM (sigh) - to do anything
281 sensible here we use the underlying Win32 calls.
284 # if defined(cygwin32_TARGET_OS)
285 /* windows.h already included */
287 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
295 vtalrm_handler(int sig)
299 For the parallel world, currentTSO is set if there is any work
300 on the current PE. In this case we DO want to context switch,
301 in case other PEs have sent us messages which must be processed.
304 # if defined(PROFILING) || defined(PAR)
305 static I_ csTicks = 0, pTicks = 0;
307 if (time_profiling) {
308 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
309 # if ! defined(PROFILING)
310 handle_tick_serial();
312 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
313 || RTSflags.ProfFlags.doHeapProfile)
314 handle_tick_serial();
316 handle_tick_noserial();
319 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
325 Handling a tick for threads blocked waiting for file
326 descriptor I/O or time.
328 This requires some care since virtual time alarm ticks
329 can occur when we are in the GC. If that is the case,
330 we just increment a delayed timer tick counter, but do
331 not check to see if any TSOs have been made runnable
332 as a result. (Do a bulk update of their status once
333 the GC has completed).
335 If the vtalrm does not occur within GC, we try to promote
336 any of the waiting threads to the runnable list (see awaitEvent)
341 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
343 else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
344 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
347 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
348 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
350 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
351 PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
352 SparkLimit[REQUIRED_POOL] / 2;
353 if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
354 PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
355 SparkLimit[ADVISORY_POOL] / 2;
356 sparksIgnored += SparkLimit[REQUIRED_POOL] / 2;
360 if (CurrentTSO != NULL ||
362 if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
364 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
365 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
366 /* ToDo: anything else for GRAN? WDP */
374 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
375 /* windows.h already included for the segv_handling above */
378 TIMECALLBACK *vtalrm_cback;
381 void (*tick_handle)(STG_NO_ARGS);
384 tick_handler(uID,uMsg,dwUser,dw1,dw2)
395 int install_vtalrm_handler()
398 vtalrm_cback = vtalrm_handler;
403 vtalrm_cback = tick_handler;
404 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
405 || RTSflags.ProfFlags.doHeapProfile)
406 tick_handle = handle_tick_serial;
408 tick_handle = handle_tick_noserial;
414 blockVtAlrmSignal(STG_NO_ARGS)
416 timeKillEvent(vtalrm_id);
420 unblockVtAlrmSignal(STG_NO_ARGS)
423 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
425 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
429 #elif defined(sunos4_TARGET_OS)
432 install_vtalrm_handler(void)
437 old = signal(SIGVTALRM, vtalrm_handler);
439 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
440 || RTSflags.ProfFlags.doHeapProfile)
441 old = signal(SIGVTALRM, handle_tick_serial);
443 old = signal(SIGVTALRM, handle_tick_noserial);
445 return ((int) old == SIG_ERR);
448 static int vtalrm_mask;
451 blockVtAlrmSignal(STG_NO_ARGS)
453 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
457 unblockVtAlrmSignal(STG_NO_ARGS)
459 (void) sigsetmask(vtalrm_mask);
462 # else /* Not SunOS 4 */
465 install_vtalrm_handler(STG_NO_ARGS)
467 struct sigaction action;
470 action.sa_handler = vtalrm_handler;
472 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
473 || RTSflags.ProfFlags.doHeapProfile)
474 action.sa_handler = handle_tick_serial;
476 action.sa_handler = handle_tick_noserial;
479 sigemptyset(&action.sa_mask);
482 return sigaction(SIGVTALRM, &action, NULL);
486 blockVtAlrmSignal(STG_NO_ARGS)
490 sigemptyset(&signals);
491 sigaddset(&signals, SIGVTALRM);
493 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
497 unblockVtAlrmSignal(STG_NO_ARGS)
501 sigemptyset(&signals);
502 sigaddset(&signals, SIGVTALRM);
504 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
507 # endif /* ! SunOS 4 */
509 #endif /* PROFILING || CONCURRENT (but not GRAN) */
513 Signal handling support for user-specified signal handlers. Since we
514 need stable pointers to do this properly, we just refuse to try in the
515 parallel world. Sorry.
519 #if defined(PAR) /* || defined(GRAN) */
522 blockUserSignals(void)
528 unblockUserSignals(void)
534 # ifdef _POSIX_SOURCE
535 sig_install(sig, spi, mask)
538 sig_install(sig, spi)
544 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
552 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
553 extern void freeStablePointer PROTO((I_));
554 extern jmp_buf restart_main;
556 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
557 static I_ nHandlers = 0; /* Size of handlers array */
560 more_handlers(I_ sig)
567 if (handlers == NULL)
568 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
570 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
572 if (handlers == NULL) {
574 fprintf(stderr, "VM exhausted (in more_handlers)\n");
577 for(i = nHandlers; i <= sig; i++)
578 /* Fill in the new slots with default actions */
579 handlers[i] = STG_SIG_DFL;
586 # ifdef _POSIX_SOURCE
589 generic_handler(int sig)
593 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
594 if (! initStacks(&StorageMgrInfo)) {
596 fprintf(stderr, "initStacks failed!\n");
599 TopClosure = deRefStablePointer(handlers[sig]);
600 sigemptyset(&signals);
601 sigaddset(&signals, sig);
602 sigprocmask(SIG_UNBLOCK, &signals, NULL);
603 longjmp(restart_main, sig);
606 static sigset_t userSignals;
607 static sigset_t savedSignals;
610 initUserSignals(void)
612 sigemptyset(&userSignals);
616 blockUserSignals(void)
618 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
622 unblockUserSignals(void)
624 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
629 sig_install(sig, spi, mask)
635 struct sigaction action;
638 /* Block the signal until we figure out what to do */
639 /* Count on this to fail if the signal number is invalid */
640 if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
641 sigprocmask(SIG_BLOCK, &signals, NULL))
646 previous_spi = handlers[sig];
650 handlers[sig] = STG_SIG_IGN;
651 sigdelset(&userSignals, sig);
652 action.sa_handler = SIG_IGN;
656 handlers[sig] = STG_SIG_DFL;
657 sigdelset(&userSignals, sig);
658 action.sa_handler = SIG_DFL;
662 sigaddset(&userSignals, sig);
663 action.sa_handler = generic_handler;
668 action.sa_mask = *mask;
670 sigemptyset(&action.sa_mask);
672 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
674 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
676 freeStablePointer(handlers[sig]);
688 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
689 if (! initStacks(&StorageMgrInfo)) {
691 fprintf(stderr, "initStacks failed!\n");
694 TopClosure = deRefStablePointer(handlers[sig]);
696 longjmp(restart_main, sig);
699 static int userSignals;
700 static int savedSignals;
703 initUserSignals(void)
709 blockUserSignals(void)
711 savedSignals = sigsetmask(userSignals);
715 unblockUserSignals(void)
717 sigsetmask(savedSignals);
721 sig_install(sig, spi)
727 void (*handler)(int);
729 /* Block the signal until we figure out what to do */
730 /* Count on this to fail if the signal number is invalid */
731 if(sig < 0 || (mask = sigmask(sig)) == 0)
734 mask = sigblock(mask);
738 previous_spi = handlers[sig];
742 handlers[sig] = STG_SIG_IGN;
743 userSignals &= ~sigmask(sig);
748 handlers[sig] = STG_SIG_DFL;
749 userSignals &= ~sigmask(sig);
754 userSignals |= sigmask(sig);
755 handler = generic_handler;
759 if (signal(sig, handler) < 0) {
761 freeStablePointer(handlers[sig]);