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 # 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 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 GetThreadContext() to get at the faulting address.
222 context.ContextFlags = CONTEXT_CONTROL;
223 hThread = GetCurrentThread(); /* cannot fail */
224 t = GetThreadContext(hThread,&context);
228 fprintf(stderr, "Segmentation fault caught, address unknown\n");
230 void *si_addr = context.Eip; /* magic */
231 if (si_addr >= (void *) stks_space
232 && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
235 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
241 install_segv_handler()
243 return (int) signal(SIGSEGV, segv_handler) == -1;
246 # else /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
248 # if defined(irix_TARGET_OS)
249 /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
250 # define si_addr _data._fault._addr
254 segv_handler(int sig, siginfo_t *sip)
255 /* NB: the second "siginfo_t" argument is not really standard */
259 fprintf(stderr, "Segmentation fault caught, address unknown\n");
261 if (sip->si_addr >= (caddr_t) stks_space
262 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
265 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
271 install_segv_handler(STG_NO_ARGS)
273 struct sigaction action;
275 action.sa_handler = segv_handler;
276 sigemptyset(&action.sa_mask);
277 action.sa_flags = SA_SIGINFO;
279 return sigaction(SIGSEGV, &action, NULL);
282 # endif /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
284 #endif /* STACK_CHECK_BY_PAGE_FAULT */
288 %************************************************************************
290 \subsection{Virtual-timer alarm (for profiling, etc.)}
292 %************************************************************************
294 The timer interrupt is somewhat simpler, and we could probably use
295 sigaction across the board, but since we have committed ourselves to
296 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
300 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
304 extern I_ delayTicks;
307 extern P_ CurrentTSO;
311 cygwin32 does not support VTALRM (sigh) - to do anything
312 sensible here we use the underlying Win32 calls.
315 # if defined(cygwin32_TARGET_OS)
316 /* windows.h already included */
318 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
326 vtalrm_handler(int sig)
330 For the parallel world, currentTSO is set if there is any work
331 on the current PE. In this case we DO want to context switch,
332 in case other PEs have sent us messages which must be processed.
335 # if defined(PROFILING) || defined(PAR)
336 static I_ csTicks = 0, pTicks = 0;
338 if (time_profiling) {
339 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
340 # if ! defined(PROFILING)
341 handle_tick_serial();
343 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
344 || RTSflags.ProfFlags.doHeapProfile)
345 handle_tick_serial();
347 handle_tick_noserial();
350 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
356 Handling a tick for threads blocked waiting for file
357 descriptor I/O or time.
359 This requires some care since virtual time alarm ticks
360 can occur when we are in the GC. If that is the case,
361 we just increment a delayed timer tick counter, but do
362 not check to see if any TSOs have been made runnable
363 as a result. (Do a bulk update of their status once
364 the GC has completed).
366 If the vtalrm does not occur within GC, we try to promote
367 any of the waiting threads to the runnable list (see awaitEvent)
372 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
374 else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
375 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
378 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
379 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
381 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
382 PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
383 SparkLimit[REQUIRED_POOL] / 2;
384 if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
385 PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
386 SparkLimit[ADVISORY_POOL] / 2;
387 sparksIgnored += SparkLimit[REQUIRED_POOL] / 2;
391 if (CurrentTSO != NULL ||
393 if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
395 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
396 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
397 /* ToDo: anything else for GRAN? WDP */
405 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
406 /* windows.h already included for the segv_handling above */
409 TIMECALLBACK *vtalrm_cback;
412 void (*tick_handle)(STG_NO_ARGS);
415 tick_handler(uID,uMsg,dwUser,dw1,dw2)
426 int install_vtalrm_handler()
429 vtalrm_cback = vtalrm_handler;
434 vtalrm_cback = tick_handler;
435 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
436 || RTSflags.ProfFlags.doHeapProfile)
437 tick_handle = handle_tick_serial;
439 tick_handle = handle_tick_noserial;
445 blockVtAlrmSignal(STG_NO_ARGS)
447 timeKillEvent(vtalrm_id);
451 unblockVtAlrmSignal(STG_NO_ARGS)
454 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
456 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
460 #elif defined(sunos4_TARGET_OS)
463 install_vtalrm_handler(void)
468 old = signal(SIGVTALRM, vtalrm_handler);
470 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
471 || RTSflags.ProfFlags.doHeapProfile)
472 old = signal(SIGVTALRM, handle_tick_serial);
474 old = signal(SIGVTALRM, handle_tick_noserial);
476 return ((int) old == SIG_ERR);
479 static int vtalrm_mask;
482 blockVtAlrmSignal(STG_NO_ARGS)
484 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
488 unblockVtAlrmSignal(STG_NO_ARGS)
490 (void) sigsetmask(vtalrm_mask);
493 # else /* Not SunOS 4 */
496 install_vtalrm_handler(STG_NO_ARGS)
498 struct sigaction action;
501 action.sa_handler = vtalrm_handler;
503 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
504 || RTSflags.ProfFlags.doHeapProfile)
505 action.sa_handler = handle_tick_serial;
507 action.sa_handler = handle_tick_noserial;
510 sigemptyset(&action.sa_mask);
513 return sigaction(SIGVTALRM, &action, NULL);
517 blockVtAlrmSignal(STG_NO_ARGS)
521 sigemptyset(&signals);
522 sigaddset(&signals, SIGVTALRM);
524 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
528 unblockVtAlrmSignal(STG_NO_ARGS)
532 sigemptyset(&signals);
533 sigaddset(&signals, SIGVTALRM);
535 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
538 # endif /* ! SunOS 4 */
540 #endif /* PROFILING || CONCURRENT (but not GRAN) */
544 Signal handling support for user-specified signal handlers. Since we
545 need stable pointers to do this properly, we just refuse to try in the
546 parallel world. Sorry.
550 #if defined(PAR) /* || defined(GRAN) */
553 blockUserSignals(void)
559 unblockUserSignals(void)
565 # ifdef _POSIX_SOURCE
566 sig_install(sig, spi, mask)
569 sig_install(sig, spi)
575 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
583 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
584 extern void freeStablePointer PROTO((I_));
585 extern jmp_buf restart_main;
587 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
588 static I_ nHandlers = 0; /* Size of handlers array */
591 more_handlers(I_ sig)
598 if (handlers == NULL)
599 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
601 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
603 if (handlers == NULL) {
605 fprintf(stderr, "VM exhausted (in more_handlers)\n");
608 for(i = nHandlers; i <= sig; i++)
609 /* Fill in the new slots with default actions */
610 handlers[i] = STG_SIG_DFL;
617 # ifdef _POSIX_SOURCE
620 generic_handler(int sig)
624 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
625 if (! initStacks(&StorageMgrInfo)) {
627 fprintf(stderr, "initStacks failed!\n");
630 TopClosure = deRefStablePointer(handlers[sig]);
631 sigemptyset(&signals);
632 sigaddset(&signals, sig);
633 sigprocmask(SIG_UNBLOCK, &signals, NULL);
634 longjmp(restart_main, sig);
637 static sigset_t userSignals;
638 static sigset_t savedSignals;
641 initUserSignals(void)
643 sigemptyset(&userSignals);
647 blockUserSignals(void)
649 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
653 unblockUserSignals(void)
655 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
660 sig_install(sig, spi, mask)
666 struct sigaction action;
669 /* Block the signal until we figure out what to do */
670 /* Count on this to fail if the signal number is invalid */
671 if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
672 sigprocmask(SIG_BLOCK, &signals, NULL))
677 previous_spi = handlers[sig];
681 handlers[sig] = STG_SIG_IGN;
682 sigdelset(&userSignals, sig);
683 action.sa_handler = SIG_IGN;
687 handlers[sig] = STG_SIG_DFL;
688 sigdelset(&userSignals, sig);
689 action.sa_handler = SIG_DFL;
693 sigaddset(&userSignals, sig);
694 action.sa_handler = generic_handler;
699 action.sa_mask = *mask;
701 sigemptyset(&action.sa_mask);
703 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
705 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
707 freeStablePointer(handlers[sig]);
719 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
720 if (! initStacks(&StorageMgrInfo)) {
722 fprintf(stderr, "initStacks failed!\n");
725 TopClosure = deRefStablePointer(handlers[sig]);
727 longjmp(restart_main, sig);
730 static int userSignals;
731 static int savedSignals;
734 initUserSignals(void)
740 blockUserSignals(void)
742 savedSignals = sigsetmask(userSignals);
746 unblockUserSignals(void)
748 sigsetmask(savedSignals);
752 sig_install(sig, spi)
758 void (*handler)(int);
760 /* Block the signal until we figure out what to do */
761 /* Count on this to fail if the signal number is invalid */
762 if(sig < 0 || (mask = sigmask(sig)) == 0)
765 mask = sigblock(mask);
769 previous_spi = handlers[sig];
773 handlers[sig] = STG_SIG_IGN;
774 userSignals &= ~sigmask(sig);
779 handlers[sig] = STG_SIG_DFL;
780 userSignals &= ~sigmask(sig);
785 userSignals |= sigmask(sig);
786 handler = generic_handler;
790 if (signal(sig, handler) < 0) {
792 freeStablePointer(handlers[sig]);