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 if (signal(SIGSEGV, segv_handler) == SIG_ERR)
170 return ((int) signal(SIGSEGV, segv_handler) == SIG_ERR);
171 /* I think the "== SIG_ERR" is saying "there was no
172 handler for SIGSEGV before this one". WDP 95/12
177 # elif defined(irix6_TARGET_OS)
180 segv_handler(int sig, siginfo_t *sip, void *dummy)
184 fprintf(stderr, "Segmentation fault caught, address unknown\n");
186 if (sip->si_addr >= (void *) stks_space
187 && sip->si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
189 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
195 install_segv_handler(STG_NO_ARGS)
197 struct sigaction action;
199 action.sa_sigaction = segv_handler;
200 sigemptyset(&action.sa_mask);
201 action.sa_flags = SA_SIGINFO;
203 return sigaction(SIGSEGV, &action, NULL);
206 # elif defined(cygwin32_TARGET_OS)
209 The signal handlers in cygwin32 (beta14) are only passed the signal
210 number, no sigcontext/siginfo is passed as event data..sigh. For
211 SIGSEGV, to get at the violating address, we need to use the Win32's
212 WaitForDebugEvent() to get out any status information.
218 /* From gdb/win32-nat.c */
220 BOOL t = WaitForDebugEvent (&event, INFINITE);
224 fprintf(stderr, "Segmentation fault caught, address unknown\n");
226 void *si_addr = event.u.Exception.ExceptionRecord.ExceptionAddress;
227 if (si_addr >= (void *) stks_space
228 && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
231 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
237 install_segv_handler()
239 return (int) signal(SIGSEGV, segv_handler) == -1;
243 # else /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
245 # if defined(irix_TARGET_OS)
246 /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
247 # define si_addr _data._fault._addr
251 segv_handler(int sig, siginfo_t *sip)
252 /* NB: the second "siginfo_t" argument is not really standard */
256 fprintf(stderr, "Segmentation fault caught, address unknown\n");
258 if (sip->si_addr >= (caddr_t) stks_space
259 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
262 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
268 install_segv_handler(STG_NO_ARGS)
270 struct sigaction action;
272 action.sa_handler = segv_handler;
273 sigemptyset(&action.sa_mask);
274 action.sa_flags = SA_SIGINFO;
276 return sigaction(SIGSEGV, &action, NULL);
279 # endif /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
281 #endif /* STACK_CHECK_BY_PAGE_FAULT */
285 %************************************************************************
287 \subsection{Virtual-timer alarm (for profiling, etc.)}
289 %************************************************************************
291 The timer interrupt is somewhat simpler, and we could probably use
292 sigaction across the board, but since we have committed ourselves to
293 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
297 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
301 extern I_ delayTicks;
304 extern P_ CurrentTSO;
308 cygwin32 does not support VTALRM (sigh) - to do anything
309 sensible here we use the underlying Win32 calls.
312 # if defined(cygwin32_TARGET_OS)
313 /* windows.h already included */
315 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
323 vtalrm_handler(int sig)
327 For the parallel world, currentTSO is set if there is any work
328 on the current PE. In this case we DO want to context switch,
329 in case other PEs have sent us messages which must be processed.
332 # if defined(PROFILING) || defined(PAR)
333 static I_ csTicks = 0, pTicks = 0;
335 if (time_profiling) {
336 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
337 # if ! defined(PROFILING)
338 handle_tick_serial();
340 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
341 || RTSflags.ProfFlags.doHeapProfile)
342 handle_tick_serial();
344 handle_tick_noserial();
347 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
353 Handling a tick for threads blocked waiting for file
354 descriptor I/O or time.
356 This requires some care since virtual time alarm ticks
357 can occur when we are in the GC. If that is the case,
358 we just increment a delayed timer tick counter, but do
359 not check to see if any TSOs have been made runnable
360 as a result. (Do a bulk update of their status once
361 the GC has completed).
363 If the vtalrm does not occur within GC, we try to promote
364 any of the waiting threads to the runnable list (see awaitEvent)
369 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
371 else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
372 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
375 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
376 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
378 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
379 PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
380 SparkLimit[REQUIRED_POOL] / 2;
381 if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
382 PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
383 SparkLimit[ADVISORY_POOL] / 2;
384 sparksIgnored += SparkLimit[REQUIRED_POOL] / 2;
388 if (CurrentTSO != NULL ||
390 if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
392 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
393 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
394 /* ToDo: anything else for GRAN? WDP */
402 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
403 /* windows.h already included for the segv_handling above */
406 TIMECALLBACK *vtalrm_cback;
409 void (*tick_handle)(STG_NO_ARGS);
412 tick_handler(uID,uMsg,dwUser,dw1,dw2)
423 int install_vtalrm_handler()
426 vtalrm_cback = vtalrm_handler;
431 vtalrm_cback = tick_handler;
432 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
433 || RTSflags.ProfFlags.doHeapProfile)
434 tick_handle = handle_tick_serial;
436 tick_handle = handle_tick_noserial;
442 blockVtAlrmSignal(STG_NO_ARGS)
444 timeKillEvent(vtalrm_id);
448 unblockVtAlrmSignal(STG_NO_ARGS)
451 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
453 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
457 #elif defined(sunos4_TARGET_OS)
460 install_vtalrm_handler(void)
465 old = signal(SIGVTALRM, vtalrm_handler);
467 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
468 || RTSflags.ProfFlags.doHeapProfile)
469 old = signal(SIGVTALRM, handle_tick_serial);
471 old = signal(SIGVTALRM, handle_tick_noserial);
473 return ((int) old == SIG_ERR);
476 static int vtalrm_mask;
479 blockVtAlrmSignal(STG_NO_ARGS)
481 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
485 unblockVtAlrmSignal(STG_NO_ARGS)
487 (void) sigsetmask(vtalrm_mask);
490 # else /* Not SunOS 4 */
493 install_vtalrm_handler(STG_NO_ARGS)
495 struct sigaction action;
498 action.sa_handler = vtalrm_handler;
500 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
501 || RTSflags.ProfFlags.doHeapProfile)
502 action.sa_handler = handle_tick_serial;
504 action.sa_handler = handle_tick_noserial;
507 sigemptyset(&action.sa_mask);
510 return sigaction(SIGVTALRM, &action, NULL);
514 blockVtAlrmSignal(STG_NO_ARGS)
518 sigemptyset(&signals);
519 sigaddset(&signals, SIGVTALRM);
521 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
525 unblockVtAlrmSignal(STG_NO_ARGS)
529 sigemptyset(&signals);
530 sigaddset(&signals, SIGVTALRM);
532 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
535 # endif /* ! SunOS 4 */
537 #endif /* PROFILING || CONCURRENT (but not GRAN) */
541 Signal handling support for user-specified signal handlers. Since we
542 need stable pointers to do this properly, we just refuse to try in the
543 parallel world. Sorry.
547 #if defined(PAR) /* || defined(GRAN) */
550 blockUserSignals(void)
556 unblockUserSignals(void)
562 # ifdef _POSIX_SOURCE
563 sig_install(sig, spi, mask)
566 sig_install(sig, spi)
572 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
580 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
581 extern void freeStablePointer PROTO((I_));
582 extern jmp_buf restart_main;
584 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
585 static I_ nHandlers = 0; /* Size of handlers array */
588 more_handlers(I_ sig)
595 if (handlers == NULL)
596 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
598 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
600 if (handlers == NULL) {
602 fprintf(stderr, "VM exhausted (in more_handlers)\n");
605 for(i = nHandlers; i <= sig; i++)
606 /* Fill in the new slots with default actions */
607 handlers[i] = STG_SIG_DFL;
614 # ifdef _POSIX_SOURCE
617 generic_handler(int sig)
621 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
622 if (! initStacks(&StorageMgrInfo)) {
624 fprintf(stderr, "initStacks failed!\n");
627 TopClosure = deRefStablePointer(handlers[sig]);
628 sigemptyset(&signals);
629 sigaddset(&signals, sig);
630 sigprocmask(SIG_UNBLOCK, &signals, NULL);
631 longjmp(restart_main, sig);
634 static sigset_t userSignals;
635 static sigset_t savedSignals;
638 initUserSignals(void)
640 sigemptyset(&userSignals);
644 blockUserSignals(void)
646 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
650 unblockUserSignals(void)
652 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
657 sig_install(sig, spi, mask)
663 struct sigaction action;
666 /* Block the signal until we figure out what to do */
667 /* Count on this to fail if the signal number is invalid */
668 if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
669 sigprocmask(SIG_BLOCK, &signals, NULL))
674 previous_spi = handlers[sig];
678 handlers[sig] = STG_SIG_IGN;
679 sigdelset(&userSignals, sig);
680 action.sa_handler = SIG_IGN;
684 handlers[sig] = STG_SIG_DFL;
685 sigdelset(&userSignals, sig);
686 action.sa_handler = SIG_DFL;
690 sigaddset(&userSignals, sig);
691 action.sa_handler = generic_handler;
696 action.sa_mask = *mask;
698 sigemptyset(&action.sa_mask);
700 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
702 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
704 freeStablePointer(handlers[sig]);
716 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
717 if (! initStacks(&StorageMgrInfo)) {
719 fprintf(stderr, "initStacks failed!\n");
722 TopClosure = deRefStablePointer(handlers[sig]);
724 longjmp(restart_main, sig);
727 static int userSignals;
728 static int savedSignals;
731 initUserSignals(void)
737 blockUserSignals(void)
739 savedSignals = sigsetmask(userSignals);
743 unblockUserSignals(void)
745 sigsetmask(savedSignals);
749 sig_install(sig, spi)
755 void (*handler)(int);
757 /* Block the signal until we figure out what to do */
758 /* Count on this to fail if the signal number is invalid */
759 if(sig < 0 || (mask = sigmask(sig)) == 0)
762 mask = sigblock(mask);
766 previous_spi = handlers[sig];
770 handlers[sig] = STG_SIG_IGN;
771 userSignals &= ~sigmask(sig);
776 handlers[sig] = STG_SIG_DFL;
777 userSignals &= ~sigmask(sig);
782 userSignals |= sigmask(sig);
783 handler = generic_handler;
787 if (signal(sig, handler) < 0) {
789 freeStablePointer(handlers[sig]);