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... */
56 # include <asm/signal.h>
59 #if defined(HAVE_SIGINFO_H)
60 /* DEC OSF1 seems to need this explicitly. Maybe others do as well? */
64 #if defined(cygwin32_TARGET_OS)
70 %************************************************************************
72 \subsection{Stack-check by protected-memory-faulting}
74 %************************************************************************
76 If we are checking stack overflow by page faulting, then we need to be
77 able to install a @SIGSEGV@ handler, preferably one which can
78 determine where the fault occurred, so that we can satisfy ourselves
79 that it really was a stack overflow and not some random segmentation
83 #if STACK_CHECK_BY_PAGE_FAULT
85 extern P_ stks_space; /* Where the stacks live, from SMstacks.lc */
88 SunOS 4.x is too old to have @SA_SIGINFO@ as a flag to @sigaction@, so
89 we use the older @signal@ call instead. This means that we also have
90 to set up the handler to expect a different collection of arguments.
94 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS) \
95 || defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
99 /* NB: all except first argument are "implementation defined" */
100 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS)
101 int code, struct sigcontext *scp, caddr_t addr)
103 struct sigcontext_struct scp)
106 extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
108 # if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
109 caddr_t addr = scp.cr2;
110 /* Magic info from Tommy Thorn! */
113 if (addr >= (caddr_t) stks_space
114 && addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
118 fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
123 install_segv_handler(void)
125 #if freebsd_TARGET_OS
126 /* FreeBSD seems to generate SIGBUS for stack overflows */
127 if (signal(SIGBUS, segv_handler) == SIG_ERR)
129 return ((int) signal(SIGSEGV, segv_handler));
131 return ((int) signal(SIGSEGV, segv_handler) == SIG_ERR);
132 /* I think the "== SIG_ERR" is saying "there was no
133 handler for SIGSEGV before this one". WDP 95/12
138 # else /* Not SunOS 4, FreeBSD, or Linux(a.out) */
140 # if defined(irix_TARGET_OS)
141 /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
142 # define si_addr _data._fault._addr
145 #if defined(cygwin32_TARGET_OS)
147 The signal handlers in cygwin32 (beta14) are only passed the signal
148 number, no sigcontext/siginfo is passed as event data..sigh. For
149 SIGSEGV, to get at the violating address, we need to use the Win32's
150 WaitForDebugEvent() to get out any status information.
156 /* From gdb/win32-nat.c */
158 BOOL t = TRUE; /* WaitForDebugEvent (&event, INFINITE); */
162 fprintf(stderr, "Segmentation fault caught, address unknown\n");
164 void *si_addr = event.u.Exception.ExceptionRecord.ExceptionAddress;
165 if (si_addr >= (void *) stks_space
166 && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
169 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
175 install_segv_handler()
177 return (int) signal(SIGSEGV, segv_handler) == -1;
181 #else /* !defined(cygwin32_TARGET_OS) */
184 segv_handler(int sig, siginfo_t *sip)
185 /* NB: the second "siginfo_t" argument is not really standard */
189 fprintf(stderr, "Segmentation fault caught, address unknown\n");
191 if (sip->si_addr >= (caddr_t) stks_space
192 && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
195 fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
201 install_segv_handler(STG_NO_ARGS)
203 struct sigaction action;
205 action.sa_handler = segv_handler;
206 sigemptyset(&action.sa_mask);
207 action.sa_flags = SA_SIGINFO;
209 return sigaction(SIGSEGV, &action, NULL);
212 #endif /* not cygwin32_TARGET_OS */
214 # endif /* not SunOS 4 */
216 #endif /* STACK_CHECK_BY_PAGE_FAULT */
220 %************************************************************************
222 \subsection{Virtual-timer alarm (for profiling, etc.)}
224 %************************************************************************
226 The timer interrupt is somewhat simpler, and we could probably use
227 sigaction across the board, but since we have committed ourselves to
228 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
232 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
236 extern I_ delayTicks;
239 extern P_ CurrentTSO;
243 cygwin32 does not support VTALRM (sigh) - to do anything
244 sensible here we use the underlying Win32 calls.
247 # if defined(cygwin32_TARGET_OS)
248 /* windows.h already included */
250 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
258 vtalrm_handler(int sig)
262 For the parallel world, currentTSO is set if there is any work
263 on the current PE. In this case we DO want to context switch,
264 in case other PEs have sent us messages which must be processed.
267 # if defined(PROFILING) || defined(PAR)
268 static I_ csTicks = 0, pTicks = 0;
270 if (time_profiling) {
271 if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
272 # if ! defined(PROFILING)
273 handle_tick_serial();
275 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
276 || RTSflags.ProfFlags.doHeapProfile)
277 handle_tick_serial();
279 handle_tick_noserial();
282 if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
288 Handling a tick for threads blocked waiting for file
289 descriptor I/O or time.
291 This requires some care since virtual time alarm ticks
292 can occur when we are in the GC. If that is the case,
293 we just increment a delayed timer tick counter, but do
294 not check to see if any TSOs have been made runnable
295 as a result. (Do a bulk update of their status once
296 the GC has completed).
298 If the vtalrm does not occur within GC, we try to promote
299 any of the waiting threads to the runnable list (see awaitEvent)
304 if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
306 else if (WaitingThreadsHd != Prelude_Z91Z93_closure)
307 AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
310 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
311 PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
313 if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL])
314 PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
315 SparkLimit[REQUIRED_POOL] / 2;
316 if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
317 PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
318 SparkLimit[ADVISORY_POOL] / 2;
319 sparksIgnored += SparkLimit[REQUIRED_POOL] / 2;
323 if (CurrentTSO != NULL ||
325 if (RunnableThreadsHd != Prelude_Z91Z93_closure ||
327 PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
328 PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
329 /* ToDo: anything else for GRAN? WDP */
337 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
338 /* windows.h already included for the segv_handling above */
341 TIMECALLBACK *vtalrm_cback;
344 void (*tick_handle)(STG_NO_ARGS);
347 tick_handler(uID,uMsg,dwUser,dw1,dw2)
358 int install_vtalrm_handler()
361 vtalrm_cback = vtalrm_handler;
366 vtalrm_cback = tick_handler;
367 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
368 || RTSflags.ProfFlags.doHeapProfile)
369 tick_handle = handle_tick_serial;
371 tick_handle = handle_tick_noserial;
377 blockVtAlrmSignal(STG_NO_ARGS)
379 timeKillEvent(vtalrm_id);
383 unblockVtAlrmSignal(STG_NO_ARGS)
386 timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
388 timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
392 #elif defined(sunos4_TARGET_OS)
395 install_vtalrm_handler(void)
400 old = signal(SIGVTALRM, vtalrm_handler);
402 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
403 || RTSflags.ProfFlags.doHeapProfile)
404 old = signal(SIGVTALRM, handle_tick_serial);
406 old = signal(SIGVTALRM, handle_tick_noserial);
408 return ((int) old == SIG_ERR);
411 static int vtalrm_mask;
414 blockVtAlrmSignal(STG_NO_ARGS)
416 vtalrm_mask = sigblock(sigmask(SIGVTALRM));
420 unblockVtAlrmSignal(STG_NO_ARGS)
422 (void) sigsetmask(vtalrm_mask);
425 # else /* Not SunOS 4 */
428 install_vtalrm_handler(STG_NO_ARGS)
430 struct sigaction action;
433 action.sa_handler = vtalrm_handler;
435 if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
436 || RTSflags.ProfFlags.doHeapProfile)
437 action.sa_handler = handle_tick_serial;
439 action.sa_handler = handle_tick_noserial;
442 sigemptyset(&action.sa_mask);
445 return sigaction(SIGVTALRM, &action, NULL);
449 blockVtAlrmSignal(STG_NO_ARGS)
453 sigemptyset(&signals);
454 sigaddset(&signals, SIGVTALRM);
456 (void) sigprocmask(SIG_BLOCK, &signals, NULL);
460 unblockVtAlrmSignal(STG_NO_ARGS)
464 sigemptyset(&signals);
465 sigaddset(&signals, SIGVTALRM);
467 (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
470 # endif /* ! SunOS 4 */
472 #endif /* PROFILING || CONCURRENT (but not GRAN) */
476 Signal handling support for user-specified signal handlers. Since we
477 need stable pointers to do this properly, we just refuse to try in the
478 parallel world. Sorry.
482 #if defined(PAR) /* || defined(GRAN) */
485 blockUserSignals(void)
491 unblockUserSignals(void)
497 # ifdef _POSIX_SOURCE
498 sig_install(sig, spi, mask)
501 sig_install(sig, spi)
507 fprintf(stderr,"No signal handling support in a parallel implementation.\n");
515 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
516 extern void freeStablePointer PROTO((I_));
517 extern jmp_buf restart_main;
519 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
520 static I_ nHandlers = 0; /* Size of handlers array */
523 more_handlers(I_ sig)
530 if (handlers == NULL)
531 handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
533 handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
535 if (handlers == NULL) {
537 fprintf(stderr, "VM exhausted (in more_handlers)\n");
540 for(i = nHandlers; i <= sig; i++)
541 /* Fill in the new slots with default actions */
542 handlers[i] = STG_SIG_DFL;
549 # ifdef _POSIX_SOURCE
552 generic_handler(int sig)
556 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
557 if (! initStacks(&StorageMgrInfo)) {
559 fprintf(stderr, "initStacks failed!\n");
562 TopClosure = deRefStablePointer(handlers[sig]);
563 sigemptyset(&signals);
564 sigaddset(&signals, sig);
565 sigprocmask(SIG_UNBLOCK, &signals, NULL);
566 longjmp(restart_main, sig);
569 static sigset_t userSignals;
570 static sigset_t savedSignals;
573 initUserSignals(void)
575 sigemptyset(&userSignals);
579 blockUserSignals(void)
581 sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
585 unblockUserSignals(void)
587 sigprocmask(SIG_SETMASK, &savedSignals, NULL);
592 sig_install(sig, spi, mask)
598 struct sigaction action;
601 /* Block the signal until we figure out what to do */
602 /* Count on this to fail if the signal number is invalid */
603 if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
604 sigprocmask(SIG_BLOCK, &signals, NULL))
609 previous_spi = handlers[sig];
613 handlers[sig] = STG_SIG_IGN;
614 sigdelset(&userSignals, sig);
615 action.sa_handler = SIG_IGN;
619 handlers[sig] = STG_SIG_DFL;
620 sigdelset(&userSignals, sig);
621 action.sa_handler = SIG_DFL;
625 sigaddset(&userSignals, sig);
626 action.sa_handler = generic_handler;
631 action.sa_mask = *mask;
633 sigemptyset(&action.sa_mask);
635 action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
637 if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
639 freeStablePointer(handlers[sig]);
651 SAVE_Hp = SAVE_HpLim; /* Just to be safe */
652 if (! initStacks(&StorageMgrInfo)) {
654 fprintf(stderr, "initStacks failed!\n");
657 TopClosure = deRefStablePointer(handlers[sig]);
659 longjmp(restart_main, sig);
662 static int userSignals;
663 static int savedSignals;
666 initUserSignals(void)
672 blockUserSignals(void)
674 savedSignals = sigsetmask(userSignals);
678 unblockUserSignals(void)
680 sigsetmask(savedSignals);
684 sig_install(sig, spi)
690 void (*handler)(int);
692 /* Block the signal until we figure out what to do */
693 /* Count on this to fail if the signal number is invalid */
694 if(sig < 0 || (mask = sigmask(sig)) == 0)
697 mask = sigblock(mask);
701 previous_spi = handlers[sig];
705 handlers[sig] = STG_SIG_IGN;
706 userSignals &= ~sigmask(sig);
711 handlers[sig] = STG_SIG_DFL;
712 userSignals &= ~sigmask(sig);
717 userSignals |= sigmask(sig);
718 handler = generic_handler;
722 if (signal(sig, handler) < 0) {
724 freeStablePointer(handlers[sig]);