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)
127 segv_handler(int sig,
128 /* NB: all except first argument are "implementation defined" */
129 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS)
130 int code, struct sigcontext *scp, caddr_t addr)
132 struct sigcontext_struct scp)
135 extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
137 # if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
138 caddr_t addr = scp.cr2;
139 /* Magic info from Tommy Thorn! */
142 if (addr >= (caddr_t) stks_space
143 && addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
147 fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
152 install_segv_handler(void)
154 #if freebsd_TARGET_OS
155 /* FreeBSD seems to generate SIGBUS for stack overflows */
156 if (signal(SIGBUS, segv_handler) == SIG_ERR)
158 return ((int) signal(SIGSEGV, segv_handler));
160 return ((int) signal(SIGSEGV, segv_handler) == SIG_ERR);
161 /* I think the "== SIG_ERR" is saying "there was no
162 handler for SIGSEGV before this one". WDP 95/12
167 # else /* Not SunOS 4, FreeBSD, or Linux(a.out) */
169 # if defined(irix_TARGET_OS)
170 /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
171 # define si_addr _data._fault._addr
174 #if defined(cygwin32_TARGET_OS)
176 The signal handlers in cygwin32 (beta14) are only passed the signal
177 number, no sigcontext/siginfo is passed as event data..sigh. For
178 SIGSEGV, to get at the violating address, we need to use the Win32's
179 WaitForDebugEvent() to get out any status information.
185 /* From gdb/win32-nat.c */
187 BOOL t = WaitForDebugEvent (&event, INFINITE);
191 fprintf(stderr, "Segmentation fault caught, address unknown\n");
193 void *si_addr = event.u.Exception.ExceptionRecord.ExceptionAddress;
194 if (si_addr >= (void *) stks_space
195 && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
198 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
204 install_segv_handler()
206 return (int) signal(SIGSEGV, segv_handler) == -1;
210 #else /* !defined(cygwin32_TARGET_OS) */
213 segv_handler(int sig, siginfo_t *sip)
214 /* NB: the second "siginfo_t" argument is not really standard */
218 fprintf(stderr, "Segmentation fault caught, address unknown\n");
220 if (sip->si_addr >= (caddr_t) stks_space
221 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
224 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
230 install_segv_handler(STG_NO_ARGS)
232 struct sigaction action;
234 action.sa_handler = segv_handler;
235 sigemptyset(&action.sa_mask);
236 action.sa_flags = SA_SIGINFO;
238 return sigaction(SIGSEGV, &action, NULL);
241 #endif /* not cygwin32_TARGET_OS */
243 # endif /* not SunOS 4 */
245 #endif /* STACK_CHECK_BY_PAGE_FAULT */
249 %************************************************************************
251 \subsection{Virtual-timer alarm (for profiling, etc.)}
253 %************************************************************************
255 The timer interrupt is somewhat simpler, and we could probably use
256 sigaction across the board, but since we have committed ourselves to
257 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
261 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
265 extern I_ delayTicks;
268 extern P_ CurrentTSO;
272 cygwin32 does not support VTALRM (sigh) - to do anything
273 sensible here we use the underlying Win32 calls.
276 # if defined(cygwin32_TARGET_OS)
277 /* windows.h already included */
279 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
287 vtalrm_handler(int sig)
291 For the parallel world, currentTSO is set if there is any work
292 on the current PE. In this case we DO want to context switch,
293 in case other PEs have sent us messages which must be processed.
296 # if defined(PROFILING) || defined(PAR)
297 static I_ csTicks = 0, pTicks = 0;
299 if (time_profiling) {
300 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
301 # if ! defined(PROFILING)
302 handle_tick_serial();
304 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
305 || RTSflags.ProfFlags.doHeapProfile)
306 handle_tick_serial();
308 handle_tick_noserial();
311 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
317 Handling a tick for threads blocked waiting for file
318 descriptor I/O or time.
320 This requires some care since virtual time alarm ticks
321 can occur when we are in the GC. If that is the case,
322 we just increment a delayed timer tick counter, but do
323 not check to see if any TSOs have been made runnable
324 as a result. (Do a bulk update of their status once
325 the GC has completed).
327 If the vtalrm does not occur within GC, we try to promote
328 any of the waiting threads to the runnable list (see awaitEvent)
333 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
335 else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
336 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
339 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
340 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
342 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
343 PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
344 SparkLimit[REQUIRED_POOL] / 2;
345 if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
346 PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
347 SparkLimit[ADVISORY_POOL] / 2;
348 sparksIgnored += SparkLimit[REQUIRED_POOL] / 2;
352 if (CurrentTSO != NULL ||
354 if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
356 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
357 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
358 /* ToDo: anything else for GRAN? WDP */
366 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
367 /* windows.h already included for the segv_handling above */
370 TIMECALLBACK *vtalrm_cback;
373 void (*tick_handle)(STG_NO_ARGS);
376 tick_handler(uID,uMsg,dwUser,dw1,dw2)
387 int install_vtalrm_handler()
390 vtalrm_cback = vtalrm_handler;
395 vtalrm_cback = tick_handler;
396 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
397 || RTSflags.ProfFlags.doHeapProfile)
398 tick_handle = handle_tick_serial;
400 tick_handle = handle_tick_noserial;
406 blockVtAlrmSignal(STG_NO_ARGS)
408 timeKillEvent(vtalrm_id);
412 unblockVtAlrmSignal(STG_NO_ARGS)
415 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
417 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
421 #elif defined(sunos4_TARGET_OS)
424 install_vtalrm_handler(void)
429 old = signal(SIGVTALRM, vtalrm_handler);
431 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
432 || RTSflags.ProfFlags.doHeapProfile)
433 old = signal(SIGVTALRM, handle_tick_serial);
435 old = signal(SIGVTALRM, handle_tick_noserial);
437 return ((int) old == SIG_ERR);
440 static int vtalrm_mask;
443 blockVtAlrmSignal(STG_NO_ARGS)
445 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
449 unblockVtAlrmSignal(STG_NO_ARGS)
451 (void) sigsetmask(vtalrm_mask);
454 # else /* Not SunOS 4 */
457 install_vtalrm_handler(STG_NO_ARGS)
459 struct sigaction action;
462 action.sa_handler = vtalrm_handler;
464 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
465 || RTSflags.ProfFlags.doHeapProfile)
466 action.sa_handler = handle_tick_serial;
468 action.sa_handler = handle_tick_noserial;
471 sigemptyset(&action.sa_mask);
474 return sigaction(SIGVTALRM, &action, NULL);
478 blockVtAlrmSignal(STG_NO_ARGS)
482 sigemptyset(&signals);
483 sigaddset(&signals, SIGVTALRM);
485 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
489 unblockVtAlrmSignal(STG_NO_ARGS)
493 sigemptyset(&signals);
494 sigaddset(&signals, SIGVTALRM);
496 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
499 # endif /* ! SunOS 4 */
501 #endif /* PROFILING || CONCURRENT (but not GRAN) */
505 Signal handling support for user-specified signal handlers. Since we
506 need stable pointers to do this properly, we just refuse to try in the
507 parallel world. Sorry.
511 #if defined(PAR) /* || defined(GRAN) */
514 blockUserSignals(void)
520 unblockUserSignals(void)
526 # ifdef _POSIX_SOURCE
527 sig_install(sig, spi, mask)
530 sig_install(sig, spi)
536 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
544 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
545 extern void freeStablePointer PROTO((I_));
546 extern jmp_buf restart_main;
548 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
549 static I_ nHandlers = 0; /* Size of handlers array */
552 more_handlers(I_ sig)
559 if (handlers == NULL)
560 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
562 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
564 if (handlers == NULL) {
566 fprintf(stderr, "VM exhausted (in more_handlers)\n");
569 for(i = nHandlers; i <= sig; i++)
570 /* Fill in the new slots with default actions */
571 handlers[i] = STG_SIG_DFL;
578 # ifdef _POSIX_SOURCE
581 generic_handler(int sig)
585 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
586 if (! initStacks(&StorageMgrInfo)) {
588 fprintf(stderr, "initStacks failed!\n");
591 TopClosure = deRefStablePointer(handlers[sig]);
592 sigemptyset(&signals);
593 sigaddset(&signals, sig);
594 sigprocmask(SIG_UNBLOCK, &signals, NULL);
595 longjmp(restart_main, sig);
598 static sigset_t userSignals;
599 static sigset_t savedSignals;
602 initUserSignals(void)
604 sigemptyset(&userSignals);
608 blockUserSignals(void)
610 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
614 unblockUserSignals(void)
616 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
621 sig_install(sig, spi, mask)
627 struct sigaction action;
630 /* Block the signal until we figure out what to do */
631 /* Count on this to fail if the signal number is invalid */
632 if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
633 sigprocmask(SIG_BLOCK, &signals, NULL))
638 previous_spi = handlers[sig];
642 handlers[sig] = STG_SIG_IGN;
643 sigdelset(&userSignals, sig);
644 action.sa_handler = SIG_IGN;
648 handlers[sig] = STG_SIG_DFL;
649 sigdelset(&userSignals, sig);
650 action.sa_handler = SIG_DFL;
654 sigaddset(&userSignals, sig);
655 action.sa_handler = generic_handler;
660 action.sa_mask = *mask;
662 sigemptyset(&action.sa_mask);
664 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
666 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
668 freeStablePointer(handlers[sig]);
680 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
681 if (! initStacks(&StorageMgrInfo)) {
683 fprintf(stderr, "initStacks failed!\n");
686 TopClosure = deRefStablePointer(handlers[sig]);
688 longjmp(restart_main, sig);
691 static int userSignals;
692 static int savedSignals;
695 initUserSignals(void)
701 blockUserSignals(void)
703 savedSignals = sigsetmask(userSignals);
707 unblockUserSignals(void)
709 sigsetmask(savedSignals);
713 sig_install(sig, spi)
719 void (*handler)(int);
721 /* Block the signal until we figure out what to do */
722 /* Count on this to fail if the signal number is invalid */
723 if(sig < 0 || (mask = sigmask(sig)) == 0)
726 mask = sigblock(mask);
730 previous_spi = handlers[sig];
734 handlers[sig] = STG_SIG_IGN;
735 userSignals &= ~sigmask(sig);
740 handlers[sig] = STG_SIG_DFL;
741 userSignals &= ~sigmask(sig);
746 userSignals |= sigmask(sig);
747 handler = generic_handler;
751 if (signal(sig, handler) < 0) {
753 freeStablePointer(handlers[sig]);