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(osf3_TARGET_OS) || 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 /* Snaffled from drivers/scsi/eata.c in 2.0.30 sources */
80 #define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
81 # if ( LINUX_VERSION_CODE < LinuxVersionCode(2,0,0) )
82 # include <asm/signal.h>
84 # include <asm/sigcontext.h>
89 #if defined(HAVE_SIGINFO_H)
90 /* DEC OSF1 seems to need this explicitly. Maybe others do as well? */
94 #if defined(cygwin32_TARGET_OS)
100 %************************************************************************
102 \subsection{Stack-check by protected-memory-faulting}
104 %************************************************************************
106 If we are checking stack overflow by page faulting, then we need to be
107 able to install a @SIGSEGV@ handler, preferably one which can
108 determine where the fault occurred, so that we can satisfy ourselves
109 that it really was a stack overflow and not some random segmentation
113 #if STACK_CHECK_BY_PAGE_FAULT
114 /* NB: At the moment, this is always false on nextstep3. CaS. */
116 extern P_ stks_space; /* Where the stacks live, from SMstacks.lc */
119 SunOS 4.x is too old to have @SA_SIGINFO@ as a flag to @sigaction@, so
120 we use the older @signal@ call instead. This means that we also have
121 to set up the handler to expect a different collection of arguments.
125 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS) \
126 || defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS) \
127 || defined(aix_TARGET_OS)
130 segv_handler(int sig,
131 /* NB: all except first argument are "implementation defined" */
132 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS)
133 int code, struct sigcontext *scp, caddr_t addr)
134 # else /* linux || aix */
135 # if defined(aix_TARGET_OS)
136 int code, struct sigcontext *scp)
138 /* sigcontext_struct has been renamed to sigcontext. If
139 compiling this code elicits a bunch of warnings about
140 "struct sigcontext_struct" being undeclared, check to
141 see whether you've got "struct sigcontext" in <asm/sigcontext.h>.
144 If you do, lower the version number below to fit the version
145 you're running (and pass us a note saying that you had to - thx!)
147 # if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,51)
148 /* sigcontext_struct has been renamed to sigcontext */
149 struct sigcontext scp)
151 struct sigcontext_struct scp)
156 extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
158 # if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
159 unsigned long addr = scp.cr2;
160 /* Magic info from Tommy Thorn! */
162 # if defined(aix_TARGET_OS)
163 caddr_t addr = scp->sc_jmpbuf.jmp_context.o_vaddr;
164 /* Magic guess by andre */
166 if ( (char *)addr >= (char *)stks_space
167 && (char *)addr < (char *)(stks_space + RTSflags.GcFlags.stksSize))
171 fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
176 install_segv_handler(void)
178 #if freebsd_TARGET_OS
179 /* FreeBSD seems to generate SIGBUS for stack overflows */
180 if (signal(SIGBUS, segv_handler) == SIG_ERR)
182 if (signal(SIGSEGV, segv_handler) == SIG_ERR)
186 return ((int) signal(SIGSEGV, segv_handler) == SIG_ERR);
187 /* I think the "== SIG_ERR" is saying "there was no
188 handler for SIGSEGV before this one". WDP 95/12
193 # elif defined(irix6_TARGET_OS)
196 segv_handler(int sig, siginfo_t *sip, void *dummy)
200 fprintf(stderr, "Segmentation fault caught, address unknown\n");
202 if (sip->si_addr >= (void *) stks_space
203 && sip->si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
205 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
211 install_segv_handler(STG_NO_ARGS)
213 struct sigaction action;
215 action.sa_sigaction = segv_handler;
216 sigemptyset(&action.sa_mask);
217 action.sa_flags = SA_SIGINFO;
219 return sigaction(SIGSEGV, &action, NULL);
222 # elif defined(cygwin32_TARGET_OS)
225 The signal handlers in cygwin32 are only passed the signal
226 number, no sigcontext/siginfo is passed as event data..sigh. For
227 SIGSEGV, to get at the violating address, we need to use the Win32's
228 GetThreadContext() to get at the faulting address.
238 context.ContextFlags = CONTEXT_CONTROL;
239 hThread = GetCurrentThread(); /* cannot fail */
240 t = GetThreadContext(hThread,&context);
244 fprintf(stderr, "Segmentation fault caught, address unknown\n");
246 void *si_addr = context.Eip; /* magic */
247 if (si_addr >= (void *) stks_space
248 && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
251 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
257 install_segv_handler()
259 return (int) signal(SIGSEGV, segv_handler) == -1;
262 # else /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
264 # if defined(irix_TARGET_OS)
265 /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
266 # define si_addr _data._fault._addr
270 segv_handler(int sig, siginfo_t *sip)
271 /* NB: the second "siginfo_t" argument is not really standard */
275 fprintf(stderr, "Segmentation fault caught, address unknown\n");
277 if (sip->si_addr >= (caddr_t) stks_space
278 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
281 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
287 install_segv_handler(STG_NO_ARGS)
289 struct sigaction action;
291 action.sa_handler = segv_handler;
292 sigemptyset(&action.sa_mask);
293 action.sa_flags = SA_SIGINFO;
295 return sigaction(SIGSEGV, &action, NULL);
298 # endif /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
300 #endif /* STACK_CHECK_BY_PAGE_FAULT */
304 %************************************************************************
306 \subsection{Virtual-timer alarm (for profiling, etc.)}
308 %************************************************************************
310 The timer interrupt is somewhat simpler, and we could probably use
311 sigaction across the board, but since we have committed ourselves to
312 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
316 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
320 extern I_ delayTicks;
323 extern P_ CurrentTSO;
327 cygwin32 does not support VTALRM (sigh) - to do anything
328 sensible here we use the underlying Win32 calls.
331 # if defined(cygwin32_TARGET_OS)
332 /* windows.h already included */
334 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
342 vtalrm_handler(int sig)
346 For the parallel world, currentTSO is set if there is any work
347 on the current PE. In this case we DO want to context switch,
348 in case other PEs have sent us messages which must be processed.
351 # if defined(PROFILING) || defined(PAR)
352 static I_ csTicks = 0, pTicks = 0;
354 if (time_profiling) {
355 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
356 # if ! defined(PROFILING)
357 handle_tick_serial();
359 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
360 || RTSflags.ProfFlags.doHeapProfile)
361 handle_tick_serial();
363 handle_tick_noserial();
366 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
372 Handling a tick for threads blocked waiting for file
373 descriptor I/O or time.
375 This requires some care since virtual time alarm ticks
376 can occur when we are in the GC. If that is the case,
377 we just increment a delayed timer tick counter, but do
378 not check to see if any TSOs have been made runnable
379 as a result. (Do a bulk update of their status once
380 the GC has completed).
382 If the vtalrm does not occur within GC, we try to promote
383 any of the waiting threads to the runnable list (see awaitEvent)
388 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
390 else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
391 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
394 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
395 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
397 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
398 PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
399 SparkLimit[REQUIRED_POOL] / 2;
400 if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
401 PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
402 SparkLimit[ADVISORY_POOL] / 2;
403 sparksIgnored += SparkLimit[REQUIRED_POOL] / 2;
407 if (CurrentTSO != NULL ||
409 if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
411 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
412 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
413 /* ToDo: anything else for GRAN? WDP */
421 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
422 /* windows.h already included for the segv_handling above */
425 TIMECALLBACK *vtalrm_cback;
428 void (*tick_handle)(STG_NO_ARGS);
431 tick_handler(uID,uMsg,dwUser,dw1,dw2)
442 int install_vtalrm_handler()
445 vtalrm_cback = vtalrm_handler;
450 vtalrm_cback = tick_handler;
451 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
452 || RTSflags.ProfFlags.doHeapProfile)
453 tick_handle = handle_tick_serial;
455 tick_handle = handle_tick_noserial;
461 blockVtAlrmSignal(STG_NO_ARGS)
463 timeKillEvent(vtalrm_id);
467 unblockVtAlrmSignal(STG_NO_ARGS)
470 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
472 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
476 #elif defined(sunos4_TARGET_OS)
479 install_vtalrm_handler(void)
484 old = signal(SIGVTALRM, vtalrm_handler);
486 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
487 || RTSflags.ProfFlags.doHeapProfile)
488 old = signal(SIGVTALRM, handle_tick_serial);
490 old = signal(SIGVTALRM, handle_tick_noserial);
492 return ((int) old == SIG_ERR);
495 static int vtalrm_mask;
498 blockVtAlrmSignal(STG_NO_ARGS)
500 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
504 unblockVtAlrmSignal(STG_NO_ARGS)
506 (void) sigsetmask(vtalrm_mask);
509 # else /* Not SunOS 4 */
512 install_vtalrm_handler(STG_NO_ARGS)
514 struct sigaction action;
517 action.sa_handler = vtalrm_handler;
519 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
520 || RTSflags.ProfFlags.doHeapProfile)
521 action.sa_handler = handle_tick_serial;
523 action.sa_handler = handle_tick_noserial;
526 sigemptyset(&action.sa_mask);
529 return sigaction(SIGVTALRM, &action, NULL);
533 blockVtAlrmSignal(STG_NO_ARGS)
537 sigemptyset(&signals);
538 sigaddset(&signals, SIGVTALRM);
540 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
544 unblockVtAlrmSignal(STG_NO_ARGS)
548 sigemptyset(&signals);
549 sigaddset(&signals, SIGVTALRM);
551 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
554 # endif /* ! SunOS 4 */
556 #endif /* PROFILING || CONCURRENT (but not GRAN) */
560 Signal handling support for user-specified signal handlers. Since we
561 need stable pointers to do this properly, we just refuse to try in the
562 parallel world. Sorry.
566 #if defined(PAR) /* || defined(GRAN) */
569 blockUserSignals(void)
575 unblockUserSignals(void)
581 # ifdef _POSIX_SOURCE
582 sig_install(sig, spi, mask)
585 sig_install(sig, spi)
591 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
599 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
600 extern void freeStablePointer PROTO((I_));
601 extern jmp_buf restart_main;
603 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
604 static I_ nHandlers = 0; /* Size of handlers array */
607 more_handlers(I_ sig)
614 if (handlers == NULL)
615 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
617 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
619 if (handlers == NULL) {
621 fprintf(stderr, "VM exhausted (in more_handlers)\n");
624 for(i = nHandlers; i <= sig; i++)
625 /* Fill in the new slots with default actions */
626 handlers[i] = STG_SIG_DFL;
633 # ifdef _POSIX_SOURCE
636 generic_handler(int sig)
640 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
641 if (! initStacks(&StorageMgrInfo)) {
643 fprintf(stderr, "initStacks failed!\n");
646 TopClosure = deRefStablePointer(handlers[sig]);
647 sigemptyset(&signals);
648 sigaddset(&signals, sig);
649 sigprocmask(SIG_UNBLOCK, &signals, NULL);
650 longjmp(restart_main, sig);
653 static sigset_t userSignals;
654 static sigset_t savedSignals;
657 initUserSignals(void)
659 sigemptyset(&userSignals);
663 blockUserSignals(void)
665 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
669 unblockUserSignals(void)
671 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
676 sig_install(sig, spi, mask)
682 struct sigaction action;
685 /* Block the signal until we figure out what to do */
686 /* Count on this to fail if the signal number is invalid */
687 if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
688 sigprocmask(SIG_BLOCK, &signals, NULL))
693 previous_spi = handlers[sig];
697 handlers[sig] = STG_SIG_IGN;
698 sigdelset(&userSignals, sig);
699 action.sa_handler = SIG_IGN;
703 handlers[sig] = STG_SIG_DFL;
704 sigdelset(&userSignals, sig);
705 action.sa_handler = SIG_DFL;
709 sigaddset(&userSignals, sig);
710 action.sa_handler = generic_handler;
715 action.sa_mask = *mask;
717 sigemptyset(&action.sa_mask);
719 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
721 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
723 freeStablePointer(handlers[sig]);
735 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
736 if (! initStacks(&StorageMgrInfo)) {
738 fprintf(stderr, "initStacks failed!\n");
741 TopClosure = deRefStablePointer(handlers[sig]);
743 longjmp(restart_main, sig);
746 static int userSignals;
747 static int savedSignals;
750 initUserSignals(void)
756 blockUserSignals(void)
758 savedSignals = sigsetmask(userSignals);
762 unblockUserSignals(void)
764 sigsetmask(savedSignals);
768 sig_install(sig, spi)
774 void (*handler)(int);
776 /* Block the signal until we figure out what to do */
777 /* Count on this to fail if the signal number is invalid */
778 if(sig < 0 || (mask = sigmask(sig)) == 0)
781 mask = sigblock(mask);
785 previous_spi = handlers[sig];
789 handlers[sig] = STG_SIG_IGN;
790 userSignals &= ~sigmask(sig);
795 handlers[sig] = STG_SIG_DFL;
796 userSignals &= ~sigmask(sig);
801 userSignals |= sigmask(sig);
802 handler = generic_handler;
806 if (signal(sig, handler) < 0) {
808 freeStablePointer(handlers[sig]);