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 #if defined(sunos4_TARGET_OS)
25 /* The sigaction in SunOS 4.1.X does not grok SA_SIGINFO */
26 # define NON_POSIX_SOURCE
29 #if defined(freebsd_TARGET_OS)
30 # define NON_POSIX_SOURCE
33 #if defined(osf1_TARGET_OS)
34 /* The include files for OSF1 do not normally define SA_SIGINFO */
35 # define _OSF_SOURCE 1
39 /* SIGVTALRM not avail w/ POSIX_SOURCE, but worse things happen without */
40 /* SIGH: triple SIGH (WDP 95/07) */
46 #if defined(HAVE_SYS_TYPES_H)
47 # include <sys/types.h>
50 #if defined(HAVE_SIGNAL_H)
54 #if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
55 /* to look *inside* sigcontext...
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
64 # ifndef LINUX_VERSION_CODE
65 # include <linux/version.h>
67 # if (LINUX_VERSION_CODE < 0x020000)
68 # include <asm/signal.h>
70 # include <asm/sigcontext.h>
75 #if defined(HAVE_SIGINFO_H)
76 /* DEC OSF1 seems to need this explicitly. Maybe others do as well? */
80 #if defined(cygwin32_TARGET_OS)
86 %************************************************************************
88 \subsection{Stack-check by protected-memory-faulting}
90 %************************************************************************
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
99 #if STACK_CHECK_BY_PAGE_FAULT
101 extern P_ stks_space; /* Where the stacks live, from SMstacks.lc */
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.
110 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS) \
111 || defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
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)
119 struct sigcontext_struct scp)
122 extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
124 # if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
125 caddr_t addr = scp.cr2;
126 /* Magic info from Tommy Thorn! */
129 if (addr >= (caddr_t) stks_space
130 && addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
134 fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
139 install_segv_handler(void)
141 #if freebsd_TARGET_OS
142 /* FreeBSD seems to generate SIGBUS for stack overflows */
143 if (signal(SIGBUS, segv_handler) == SIG_ERR)
145 return ((int) signal(SIGSEGV, segv_handler));
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
154 # else /* Not SunOS 4, FreeBSD, or Linux(a.out) */
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
161 #if defined(cygwin32_TARGET_OS)
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.
172 /* From gdb/win32-nat.c */
174 BOOL t = WaitForDebugEvent (&event, INFINITE);
178 fprintf(stderr, "Segmentation fault caught, address unknown\n");
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))
185 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
191 install_segv_handler()
193 return (int) signal(SIGSEGV, segv_handler) == -1;
197 #else /* !defined(cygwin32_TARGET_OS) */
200 segv_handler(int sig, siginfo_t *sip)
201 /* NB: the second "siginfo_t" argument is not really standard */
205 fprintf(stderr, "Segmentation fault caught, address unknown\n");
207 if (sip->si_addr >= (caddr_t) stks_space
208 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
211 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
217 install_segv_handler(STG_NO_ARGS)
219 struct sigaction action;
221 action.sa_handler = segv_handler;
222 sigemptyset(&action.sa_mask);
223 action.sa_flags = SA_SIGINFO;
225 return sigaction(SIGSEGV, &action, NULL);
228 #endif /* not cygwin32_TARGET_OS */
230 # endif /* not SunOS 4 */
232 #endif /* STACK_CHECK_BY_PAGE_FAULT */
236 %************************************************************************
238 \subsection{Virtual-timer alarm (for profiling, etc.)}
240 %************************************************************************
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
248 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
252 extern I_ delayTicks;
255 extern P_ CurrentTSO;
259 cygwin32 does not support VTALRM (sigh) - to do anything
260 sensible here we use the underlying Win32 calls.
263 # if defined(cygwin32_TARGET_OS)
264 /* windows.h already included */
266 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
274 vtalrm_handler(int sig)
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.
283 # if defined(PROFILING) || defined(PAR)
284 static I_ csTicks = 0, pTicks = 0;
286 if (time_profiling) {
287 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
288 # if ! defined(PROFILING)
289 handle_tick_serial();
291 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
292 || RTSflags.ProfFlags.doHeapProfile)
293 handle_tick_serial();
295 handle_tick_noserial();
298 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
304 Handling a tick for threads blocked waiting for file
305 descriptor I/O or time.
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).
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)
320 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
322 else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
323 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
326 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
327 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
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;
339 if (CurrentTSO != NULL ||
341 if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
343 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
344 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
345 /* ToDo: anything else for GRAN? WDP */
353 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
354 /* windows.h already included for the segv_handling above */
357 TIMECALLBACK *vtalrm_cback;
360 void (*tick_handle)(STG_NO_ARGS);
363 tick_handler(uID,uMsg,dwUser,dw1,dw2)
374 int install_vtalrm_handler()
377 vtalrm_cback = vtalrm_handler;
382 vtalrm_cback = tick_handler;
383 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
384 || RTSflags.ProfFlags.doHeapProfile)
385 tick_handle = handle_tick_serial;
387 tick_handle = handle_tick_noserial;
393 blockVtAlrmSignal(STG_NO_ARGS)
395 timeKillEvent(vtalrm_id);
399 unblockVtAlrmSignal(STG_NO_ARGS)
402 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
404 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
408 #elif defined(sunos4_TARGET_OS)
411 install_vtalrm_handler(void)
416 old = signal(SIGVTALRM, vtalrm_handler);
418 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
419 || RTSflags.ProfFlags.doHeapProfile)
420 old = signal(SIGVTALRM, handle_tick_serial);
422 old = signal(SIGVTALRM, handle_tick_noserial);
424 return ((int) old == SIG_ERR);
427 static int vtalrm_mask;
430 blockVtAlrmSignal(STG_NO_ARGS)
432 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
436 unblockVtAlrmSignal(STG_NO_ARGS)
438 (void) sigsetmask(vtalrm_mask);
441 # else /* Not SunOS 4 */
444 install_vtalrm_handler(STG_NO_ARGS)
446 struct sigaction action;
449 action.sa_handler = vtalrm_handler;
451 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
452 || RTSflags.ProfFlags.doHeapProfile)
453 action.sa_handler = handle_tick_serial;
455 action.sa_handler = handle_tick_noserial;
458 sigemptyset(&action.sa_mask);
461 return sigaction(SIGVTALRM, &action, NULL);
465 blockVtAlrmSignal(STG_NO_ARGS)
469 sigemptyset(&signals);
470 sigaddset(&signals, SIGVTALRM);
472 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
476 unblockVtAlrmSignal(STG_NO_ARGS)
480 sigemptyset(&signals);
481 sigaddset(&signals, SIGVTALRM);
483 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
486 # endif /* ! SunOS 4 */
488 #endif /* PROFILING || CONCURRENT (but not GRAN) */
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.
498 #if defined(PAR) /* || defined(GRAN) */
501 blockUserSignals(void)
507 unblockUserSignals(void)
513 # ifdef _POSIX_SOURCE
514 sig_install(sig, spi, mask)
517 sig_install(sig, spi)
523 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
531 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
532 extern void freeStablePointer PROTO((I_));
533 extern jmp_buf restart_main;
535 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
536 static I_ nHandlers = 0; /* Size of handlers array */
539 more_handlers(I_ sig)
546 if (handlers == NULL)
547 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
549 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
551 if (handlers == NULL) {
553 fprintf(stderr, "VM exhausted (in more_handlers)\n");
556 for(i = nHandlers; i <= sig; i++)
557 /* Fill in the new slots with default actions */
558 handlers[i] = STG_SIG_DFL;
565 # ifdef _POSIX_SOURCE
568 generic_handler(int sig)
572 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
573 if (! initStacks(&StorageMgrInfo)) {
575 fprintf(stderr, "initStacks failed!\n");
578 TopClosure = deRefStablePointer(handlers[sig]);
579 sigemptyset(&signals);
580 sigaddset(&signals, sig);
581 sigprocmask(SIG_UNBLOCK, &signals, NULL);
582 longjmp(restart_main, sig);
585 static sigset_t userSignals;
586 static sigset_t savedSignals;
589 initUserSignals(void)
591 sigemptyset(&userSignals);
595 blockUserSignals(void)
597 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
601 unblockUserSignals(void)
603 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
608 sig_install(sig, spi, mask)
614 struct sigaction action;
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))
625 previous_spi = handlers[sig];
629 handlers[sig] = STG_SIG_IGN;
630 sigdelset(&userSignals, sig);
631 action.sa_handler = SIG_IGN;
635 handlers[sig] = STG_SIG_DFL;
636 sigdelset(&userSignals, sig);
637 action.sa_handler = SIG_DFL;
641 sigaddset(&userSignals, sig);
642 action.sa_handler = generic_handler;
647 action.sa_mask = *mask;
649 sigemptyset(&action.sa_mask);
651 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
653 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
655 freeStablePointer(handlers[sig]);
667 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
668 if (! initStacks(&StorageMgrInfo)) {
670 fprintf(stderr, "initStacks failed!\n");
673 TopClosure = deRefStablePointer(handlers[sig]);
675 longjmp(restart_main, sig);
678 static int userSignals;
679 static int savedSignals;
682 initUserSignals(void)
688 blockUserSignals(void)
690 savedSignals = sigsetmask(userSignals);
694 unblockUserSignals(void)
696 sigsetmask(savedSignals);
700 sig_install(sig, spi)
706 void (*handler)(int);
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)
713 mask = sigblock(mask);
717 previous_spi = handlers[sig];
721 handlers[sig] = STG_SIG_IGN;
722 userSignals &= ~sigmask(sig);
727 handlers[sig] = STG_SIG_DFL;
728 userSignals &= ~sigmask(sig);
733 userSignals |= sigmask(sig);
734 handler = generic_handler;
738 if (signal(sig, handler) < 0) {
740 freeStablePointer(handlers[sig]);