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 # ifndef irix6_TARGET_OS /* will this do for irix-6.x ? ToDo: check. (SOF 5/97) */
179 /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
180 # define si_addr _data._fault._addr
184 #if defined(cygwin32_TARGET_OS)
186 The signal handlers in cygwin32 (beta14) are only passed the signal
187 number, no sigcontext/siginfo is passed as event data..sigh. For
188 SIGSEGV, to get at the violating address, we need to use the Win32's
189 WaitForDebugEvent() to get out any status information.
195 /* From gdb/win32-nat.c */
197 BOOL t = WaitForDebugEvent (&event, INFINITE);
201 fprintf(stderr, "Segmentation fault caught, address unknown\n");
203 void *si_addr = event.u.Exception.ExceptionRecord.ExceptionAddress;
204 if (si_addr >= (void *) stks_space
205 && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
208 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
214 install_segv_handler()
216 return (int) signal(SIGSEGV, segv_handler) == -1;
220 #else /* !defined(cygwin32_TARGET_OS) */
223 segv_handler(int sig, siginfo_t *sip)
224 /* NB: the second "siginfo_t" argument is not really standard */
228 fprintf(stderr, "Segmentation fault caught, address unknown\n");
230 if (sip->si_addr >= (caddr_t) stks_space
231 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
234 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
240 install_segv_handler(STG_NO_ARGS)
242 struct sigaction action;
244 action.sa_handler = segv_handler;
245 sigemptyset(&action.sa_mask);
246 action.sa_flags = SA_SIGINFO;
248 return sigaction(SIGSEGV, &action, NULL);
251 #endif /* not cygwin32_TARGET_OS */
253 # endif /* not SunOS 4 */
255 #endif /* STACK_CHECK_BY_PAGE_FAULT */
259 %************************************************************************
261 \subsection{Virtual-timer alarm (for profiling, etc.)}
263 %************************************************************************
265 The timer interrupt is somewhat simpler, and we could probably use
266 sigaction across the board, but since we have committed ourselves to
267 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
271 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
275 extern I_ delayTicks;
278 extern P_ CurrentTSO;
282 cygwin32 does not support VTALRM (sigh) - to do anything
283 sensible here we use the underlying Win32 calls.
286 # if defined(cygwin32_TARGET_OS)
287 /* windows.h already included */
289 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
297 vtalrm_handler(int sig)
301 For the parallel world, currentTSO is set if there is any work
302 on the current PE. In this case we DO want to context switch,
303 in case other PEs have sent us messages which must be processed.
306 # if defined(PROFILING) || defined(PAR)
307 static I_ csTicks = 0, pTicks = 0;
309 if (time_profiling) {
310 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
311 # if ! defined(PROFILING)
312 handle_tick_serial();
314 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
315 || RTSflags.ProfFlags.doHeapProfile)
316 handle_tick_serial();
318 handle_tick_noserial();
321 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
327 Handling a tick for threads blocked waiting for file
328 descriptor I/O or time.
330 This requires some care since virtual time alarm ticks
331 can occur when we are in the GC. If that is the case,
332 we just increment a delayed timer tick counter, but do
333 not check to see if any TSOs have been made runnable
334 as a result. (Do a bulk update of their status once
335 the GC has completed).
337 If the vtalrm does not occur within GC, we try to promote
338 any of the waiting threads to the runnable list (see awaitEvent)
343 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
345 else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
346 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
349 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
350 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
352 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
353 PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
354 SparkLimit[REQUIRED_POOL] / 2;
355 if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
356 PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
357 SparkLimit[ADVISORY_POOL] / 2;
358 sparksIgnored += SparkLimit[REQUIRED_POOL] / 2;
362 if (CurrentTSO != NULL ||
364 if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
366 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
367 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
368 /* ToDo: anything else for GRAN? WDP */
376 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
377 /* windows.h already included for the segv_handling above */
380 TIMECALLBACK *vtalrm_cback;
383 void (*tick_handle)(STG_NO_ARGS);
386 tick_handler(uID,uMsg,dwUser,dw1,dw2)
397 int install_vtalrm_handler()
400 vtalrm_cback = vtalrm_handler;
405 vtalrm_cback = tick_handler;
406 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
407 || RTSflags.ProfFlags.doHeapProfile)
408 tick_handle = handle_tick_serial;
410 tick_handle = handle_tick_noserial;
416 blockVtAlrmSignal(STG_NO_ARGS)
418 timeKillEvent(vtalrm_id);
422 unblockVtAlrmSignal(STG_NO_ARGS)
425 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
427 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
431 #elif defined(sunos4_TARGET_OS)
434 install_vtalrm_handler(void)
439 old = signal(SIGVTALRM, vtalrm_handler);
441 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
442 || RTSflags.ProfFlags.doHeapProfile)
443 old = signal(SIGVTALRM, handle_tick_serial);
445 old = signal(SIGVTALRM, handle_tick_noserial);
447 return ((int) old == SIG_ERR);
450 static int vtalrm_mask;
453 blockVtAlrmSignal(STG_NO_ARGS)
455 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
459 unblockVtAlrmSignal(STG_NO_ARGS)
461 (void) sigsetmask(vtalrm_mask);
464 # else /* Not SunOS 4 */
467 install_vtalrm_handler(STG_NO_ARGS)
469 struct sigaction action;
472 action.sa_handler = vtalrm_handler;
474 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
475 || RTSflags.ProfFlags.doHeapProfile)
476 action.sa_handler = handle_tick_serial;
478 action.sa_handler = handle_tick_noserial;
481 sigemptyset(&action.sa_mask);
484 return sigaction(SIGVTALRM, &action, NULL);
488 blockVtAlrmSignal(STG_NO_ARGS)
492 sigemptyset(&signals);
493 sigaddset(&signals, SIGVTALRM);
495 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
499 unblockVtAlrmSignal(STG_NO_ARGS)
503 sigemptyset(&signals);
504 sigaddset(&signals, SIGVTALRM);
506 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
509 # endif /* ! SunOS 4 */
511 #endif /* PROFILING || CONCURRENT (but not GRAN) */
515 Signal handling support for user-specified signal handlers. Since we
516 need stable pointers to do this properly, we just refuse to try in the
517 parallel world. Sorry.
521 #if defined(PAR) /* || defined(GRAN) */
524 blockUserSignals(void)
530 unblockUserSignals(void)
536 # ifdef _POSIX_SOURCE
537 sig_install(sig, spi, mask)
540 sig_install(sig, spi)
546 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
554 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
555 extern void freeStablePointer PROTO((I_));
556 extern jmp_buf restart_main;
558 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
559 static I_ nHandlers = 0; /* Size of handlers array */
562 more_handlers(I_ sig)
569 if (handlers == NULL)
570 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
572 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
574 if (handlers == NULL) {
576 fprintf(stderr, "VM exhausted (in more_handlers)\n");
579 for(i = nHandlers; i <= sig; i++)
580 /* Fill in the new slots with default actions */
581 handlers[i] = STG_SIG_DFL;
588 # ifdef _POSIX_SOURCE
591 generic_handler(int sig)
595 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
596 if (! initStacks(&StorageMgrInfo)) {
598 fprintf(stderr, "initStacks failed!\n");
601 TopClosure = deRefStablePointer(handlers[sig]);
602 sigemptyset(&signals);
603 sigaddset(&signals, sig);
604 sigprocmask(SIG_UNBLOCK, &signals, NULL);
605 longjmp(restart_main, sig);
608 static sigset_t userSignals;
609 static sigset_t savedSignals;
612 initUserSignals(void)
614 sigemptyset(&userSignals);
618 blockUserSignals(void)
620 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
624 unblockUserSignals(void)
626 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
631 sig_install(sig, spi, mask)
637 struct sigaction action;
640 /* Block the signal until we figure out what to do */
641 /* Count on this to fail if the signal number is invalid */
642 if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
643 sigprocmask(SIG_BLOCK, &signals, NULL))
648 previous_spi = handlers[sig];
652 handlers[sig] = STG_SIG_IGN;
653 sigdelset(&userSignals, sig);
654 action.sa_handler = SIG_IGN;
658 handlers[sig] = STG_SIG_DFL;
659 sigdelset(&userSignals, sig);
660 action.sa_handler = SIG_DFL;
664 sigaddset(&userSignals, sig);
665 action.sa_handler = generic_handler;
670 action.sa_mask = *mask;
672 sigemptyset(&action.sa_mask);
674 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
676 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
678 freeStablePointer(handlers[sig]);
690 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
691 if (! initStacks(&StorageMgrInfo)) {
693 fprintf(stderr, "initStacks failed!\n");
696 TopClosure = deRefStablePointer(handlers[sig]);
698 longjmp(restart_main, sig);
701 static int userSignals;
702 static int savedSignals;
705 initUserSignals(void)
711 blockUserSignals(void)
713 savedSignals = sigsetmask(userSignals);
717 unblockUserSignals(void)
719 sigsetmask(savedSignals);
723 sig_install(sig, spi)
729 void (*handler)(int);
731 /* Block the signal until we figure out what to do */
732 /* Count on this to fail if the signal number is invalid */
733 if(sig < 0 || (mask = sigmask(sig)) == 0)
736 mask = sigblock(mask);
740 previous_spi = handlers[sig];
744 handlers[sig] = STG_SIG_IGN;
745 userSignals &= ~sigmask(sig);
750 handlers[sig] = STG_SIG_DFL;
751 userSignals &= ~sigmask(sig);
756 userSignals |= sigmask(sig);
757 handler = generic_handler;
761 if (signal(sig, handler) < 0) {
763 freeStablePointer(handlers[sig]);