[project @ 1996-01-11 14:06:51 by partain]
[ghc-hetmet.git] / ghc / runtime / main / Signals.lc
1 %
2 % (c) The AQUA Project, Glasgow University, 1995
3 %
4 %************************************************************************
5 %*                                                                      *
6 \section[Signals.lc]{Signal Handlers}
7 %*                                                                      *
8 %************************************************************************
9
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.
16
17 Then, there are the user-specified signal handlers to cope with.
18 Since they're pretty rudimentary, they shouldn't actually cause as
19 much pain.
20
21 \begin{code}
22 #include "platform.h"
23
24 #if defined(sunos4_TARGET_OS)
25     /* The sigaction in SunOS 4.1.X does not grok SA_SIGINFO */
26 # define NON_POSIX_SOURCE
27 #endif
28
29 #if defined(osf1_TARGET_OS)
30     /* The include files for OSF1 do not normally define SA_SIGINFO */
31 # define _OSF_SOURCE 1
32 #endif
33
34 #if irix_TARGET_OS
35 /* SIGVTALRM not avail w/ POSIX_SOURCE, but worse things happen without */
36 /* SIGH: triple SIGH (WDP 95/07) */
37 # define SIGVTALRM 28
38 #endif
39
40 #include "rtsdefs.h"
41
42 #if defined(HAVE_SYS_TYPES_H)
43 # include <sys/types.h>
44 #endif
45
46 #if defined(HAVE_SIGNAL_H)
47 # include <signal.h>
48 #endif
49
50 #if defined(HAVE_SIGINFO_H)
51     /* DEC OSF1 seems to need this explicitly.  Maybe others do as well? */
52 # include <siginfo.h>
53 #endif
54
55 \end{code}
56
57 %************************************************************************
58 %*                                                                      *
59 \subsection{Stack-check by protected-memory-faulting}
60 %*                                                                      *
61 %************************************************************************
62
63 If we are checking stack overflow by page faulting, then we need to be
64 able to install a @SIGSEGV@ handler, preferably one which can
65 determine where the fault occurred, so that we can satisfy ourselves
66 that it really was a stack overflow and not some random segmentation
67 fault.
68
69 \begin{code}
70 #if STACK_CHECK_BY_PAGE_FAULT
71
72 extern P_ stks_space;       /* Where the stacks live, from SMstacks.lc */
73 \end{code}
74
75 SunOS 4.x is too old to have @SA_SIGINFO@ as a flag to @sigaction@, so
76 we use the older @signal@ call instead.  This means that we also have
77 to set up the handler to expect a different collection of arguments.
78 Fun, eh?
79
80 \begin{code}
81 # if defined(sunos4_TARGET_OS)
82
83 static void
84 segv_handler(sig, code, scp, addr)
85   int sig;
86   int code; /* NB: all except first argument are "implementation defined" */
87   struct sigcontext *scp;
88   caddr_t addr;
89 {
90     extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
91
92     if (addr >= (caddr_t) stks_space
93       && addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
94         StackOverflow();
95
96     fflush(stdout);
97     fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
98     abort();
99 }
100
101 int
102 install_segv_handler(void)
103 {
104     return ((int) signal(SIGSEGV, segv_handler) == SIG_ERR);
105     /* I think the "== SIG_ERR" is saying "there was no
106        handler for SIGSEGV before this one".  WDP 95/12
107     */
108 }
109
110 # else  /* Not SunOS 4 */
111
112 #  if defined(irix_TARGET_OS)
113      /* certainly BOGUS (WDP 94/05) -- copied from /usr/include/sys/siginfo.h */
114 #   define si_addr _data._fault._addr
115 #  endif
116
117 static void
118 segv_handler(int sig, siginfo_t *sip)
119   /* NB: the second "siginfo_t" argument is not really standard */
120 {
121     fflush(stdout);
122     if (sip == NULL) {
123         fprintf(stderr, "Segmentation fault caught, address unknown\n");
124     } else {
125         if (sip->si_addr >= (caddr_t) stks_space
126           && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
127             StackOverflow();
128
129         fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
130     }
131     abort();
132 }
133
134 int
135 install_segv_handler(STG_NO_ARGS)
136 {
137     struct sigaction action;
138
139     action.sa_handler = segv_handler;
140     sigemptyset(&action.sa_mask);
141     action.sa_flags = SA_SIGINFO;
142
143     return sigaction(SIGSEGV, &action, NULL);
144 }
145
146 # endif    /* not SunOS 4 */
147
148 #endif  /* STACK_CHECK_BY_PAGE_FAULT */
149
150 \end{code}
151
152 %************************************************************************
153 %*                                                                      *
154 \subsection{Virtual-timer alarm (for profiling, etc.)}
155 %*                                                                      *
156 %************************************************************************
157
158 The timer interrupt is somewhat simpler, and we could probably use
159 sigaction across the board, but since we have committed ourselves to
160 the non-POSIX signal under SunOS 4.1.X, we adopt the same approach
161 here.
162
163 \begin{code}
164 #if (defined(PROFILING) || defined(CONCURRENT)) && !defined(GRAN)
165
166 # ifdef CONCURRENT
167
168 #  ifdef PAR
169 extern P_ CurrentTSO;
170 #  endif
171
172 static void
173 vtalrm_handler(int sig)
174 {
175 /*
176    For the parallel world, currentTSO is set if there is any work
177    on the current PE.  In this case we DO want to context switch,
178    in case other PEs have sent us messages which must be processed.
179 */
180
181 #  if defined(PROFILING) || defined(PAR)
182     static I_ csTicks = 0, pTicks = 0;
183
184     if (time_profiling) {
185         if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
186 #   if ! defined(PROFILING)
187             handle_tick_serial();
188 #   else
189             if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
190              || RTSflags.ProfFlags.doHeapProfile)
191                 handle_tick_serial();
192             else
193                 handle_tick_noserial();
194 #   endif
195         }
196         if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
197             return;
198     }
199 #  endif
200
201     if (WaitingThreadsHd != Nil_closure)
202         AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
203
204 #  ifdef PAR
205     if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
206       PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
207         PruneSparks();
208         if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL]) 
209             PendingSparksTl[REQUIRED_POOL] = PendingSparksBase[REQUIRED_POOL] +
210               SparkLimit[REQUIRED_POOL] / 2;
211         if (PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
212             PendingSparksTl[ADVISORY_POOL] = PendingSparksBase[ADVISORY_POOL] +
213               SparkLimit[ADVISORY_POOL] / 2;
214             sparksIgnored += SparkLimit[REQUIRED_POOL] / 2; 
215         }
216     }
217
218     if (CurrentTSO != NULL ||
219 #  else
220     if (RunnableThreadsHd != Nil_closure ||
221 #  endif
222       PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
223       PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
224         /* ToDo: anything else for GRAN? WDP */
225         context_switch = 1;
226     }
227 }
228
229 # endif
230
231 # if defined(sunos4_TARGET_OS)
232
233 int
234 install_vtalrm_handler(void)
235 {
236     void (*old)();
237
238 #  ifdef CONCURRENT
239     old = signal(SIGVTALRM, vtalrm_handler);
240 #  else
241     if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
242      || RTSflags.ProfFlags.doHeapProfile)
243         old = signal(SIGVTALRM, handle_tick_serial);
244     else
245         old = signal(SIGVTALRM, handle_tick_noserial);
246 #  endif
247     return ((int) old == SIG_ERR);
248 }
249
250 static int vtalrm_mask;
251
252 void
253 blockVtAlrmSignal(STG_NO_ARGS)
254 {
255     vtalrm_mask = sigblock(sigmask(SIGVTALRM));
256 }
257
258 void
259 unblockVtAlrmSignal(STG_NO_ARGS)
260 {
261     (void) sigsetmask(vtalrm_mask);
262 }
263
264 # else  /* Not SunOS 4 */
265
266 int
267 install_vtalrm_handler(STG_NO_ARGS)
268 {
269     struct sigaction action;
270
271 #  ifdef CONCURRENT
272     action.sa_handler = vtalrm_handler;
273 #  else
274     if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
275      || RTSflags.ProfFlags.doHeapProfile)
276         action.sa_handler = handle_tick_serial;
277     else
278         action.sa_handler = handle_tick_noserial;
279 #  endif
280
281     sigemptyset(&action.sa_mask);
282     action.sa_flags = 0;
283
284     return sigaction(SIGVTALRM, &action, NULL);
285 }
286
287 void
288 blockVtAlrmSignal(STG_NO_ARGS)
289 {
290     sigset_t signals;
291     
292     sigemptyset(&signals);
293     sigaddset(&signals, SIGVTALRM);
294
295     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
296 }
297
298 void
299 unblockVtAlrmSignal(STG_NO_ARGS)
300 {
301     sigset_t signals;
302     
303     sigemptyset(&signals);
304     sigaddset(&signals, SIGVTALRM);
305
306     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
307 }
308
309 # endif /* ! SunOS 4 */
310
311 #endif /* PROFILING || CONCURRENT (but not GRAN) */
312
313 \end{code}
314
315 Signal handling support for user-specified signal handlers.  Since we
316 need stable pointers to do this properly, we just refuse to try in the
317 parallel world.  Sorry.
318
319 \begin{code}
320
321 #ifdef PAR
322
323 void
324 blockUserSignals(void)
325 {
326     return;
327 }
328
329 void
330 unblockUserSignals(void)
331 {
332     return;
333 }
334
335 I_ 
336 # ifdef _POSIX_SOURCE
337 sig_install(sig, spi, mask)
338   sigset_t *mask;
339 # else
340   sig_install(sig, spi)
341 # endif
342   I_ sig;
343   I_ spi;
344 {
345     fflush(stdout);
346     fprintf(stderr,"No signal handling support in a parallel implementation.\n");
347     EXIT(EXIT_FAILURE);
348 }
349
350 #else   /* !PAR */
351
352 # include <setjmp.h>
353
354 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
355 extern void freeStablePointer PROTO((I_));
356 extern jmp_buf restart_main;
357
358 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
359 static I_ nHandlers = 0;    /* Size of handlers array */
360
361 static void
362 more_handlers(I_ sig)
363 {
364     I_ i;
365
366     if (sig < nHandlers)
367         return;
368
369     if (handlers == NULL)
370         handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
371     else
372         handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
373
374     if (handlers == NULL) {
375         fflush(stdout);
376         fprintf(stderr, "VM exhausted (in more_handlers)\n");
377         EXIT(EXIT_FAILURE);
378     }
379     for(i = nHandlers; i <= sig; i++)
380         /* Fill in the new slots with default actions */
381         handlers[i] = STG_SIG_DFL;
382
383     nHandlers = sig + 1;
384 }
385
386 # ifdef _POSIX_SOURCE
387
388 static void
389 generic_handler(int sig)
390 {
391     sigset_t signals;
392
393     SAVE_Hp = SAVE_HpLim;       /* Just to be safe */
394     if (! initStacks(&StorageMgrInfo)) {
395         fflush(stdout);
396         fprintf(stderr, "initStacks failed!\n");
397         EXIT(EXIT_FAILURE);
398     }
399     TopClosure = deRefStablePointer(handlers[sig]);
400     sigemptyset(&signals);
401     sigaddset(&signals, sig);
402     sigprocmask(SIG_UNBLOCK, &signals, NULL);
403     longjmp(restart_main, sig);
404 }
405
406 static sigset_t userSignals;
407 static sigset_t savedSignals;
408
409 void
410 initUserSignals(void)
411 {
412     sigemptyset(&userSignals);
413 }
414
415 void
416 blockUserSignals(void)
417 {
418     sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
419 }
420
421 void
422 unblockUserSignals(void)
423 {
424     sigprocmask(SIG_SETMASK, &savedSignals, NULL);
425 }
426
427
428 I_ nocldstop = 0;
429
430 I_ 
431 sig_install(sig, spi, mask)
432   I_ sig;
433   I_ spi;
434   sigset_t *mask;
435 {
436     sigset_t signals;
437     struct sigaction action;
438     I_ previous_spi;
439
440     /* Block the signal until we figure out what to do */
441     /* Count on this to fail if the signal number is invalid */
442     if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
443        sigprocmask(SIG_BLOCK, &signals, NULL))
444         return STG_SIG_ERR;
445
446     more_handlers(sig);
447
448     previous_spi = handlers[sig];
449
450     switch(spi) {
451     case STG_SIG_IGN:
452         handlers[sig] = STG_SIG_IGN;
453         sigdelset(&userSignals, sig);
454         action.sa_handler = SIG_IGN;
455         break;
456         
457     case STG_SIG_DFL:
458         handlers[sig] = STG_SIG_DFL;
459         sigdelset(&userSignals, sig);
460         action.sa_handler = SIG_DFL;
461         break;
462     default:
463         handlers[sig] = spi;
464         sigaddset(&userSignals, sig);
465         action.sa_handler = generic_handler;
466         break;
467     }
468
469     if (mask != NULL)
470         action.sa_mask = *mask;
471     else
472         sigemptyset(&action.sa_mask);
473
474     action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
475
476     if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
477         if (previous_spi)
478           freeStablePointer(handlers[sig]);
479         return STG_SIG_ERR;
480     }
481
482     return previous_spi;
483 }
484
485 # else  /* !POSIX */
486
487 static void
488 generic_handler(sig)
489 {
490     SAVE_Hp = SAVE_HpLim;       /* Just to be safe */
491     if (! initStacks(&StorageMgrInfo)) {
492         fflush(stdout);
493         fprintf(stderr, "initStacks failed!\n");
494         EXIT(EXIT_FAILURE);
495     }
496     TopClosure = deRefStablePointer(handlers[sig]);
497     sigsetmask(0);
498     longjmp(restart_main, sig);
499 }
500
501 static int userSignals;
502 static int savedSignals;
503
504 void
505 initUserSignals(void)
506 {
507     userSignals = 0;
508 }
509
510 void
511 blockUserSignals(void)
512 {
513     savedSignals = sigsetmask(userSignals);
514 }
515
516 void
517 unblockUserSignals(void)
518 {
519     sigsetmask(savedSignals);
520 }
521
522 I_ 
523 sig_install(sig, spi)
524   I_ sig;
525   I_ spi;
526 {
527     I_ previous_spi;
528     int mask;
529     void (*handler)(int);
530
531     /* Block the signal until we figure out what to do */
532     /* Count on this to fail if the signal number is invalid */
533     if(sig < 0 || (mask = sigmask(sig)) == 0)
534         return STG_SIG_ERR;
535
536     mask = sigblock(mask);
537
538     more_handlers(sig);
539
540     previous_spi = handlers[sig];
541
542     switch(spi) {
543     case STG_SIG_IGN:
544         handlers[sig] = STG_SIG_IGN;
545         userSignals &= ~sigmask(sig);
546         handler = SIG_IGN;
547         break;
548         
549     case STG_SIG_DFL:
550         handlers[sig] = STG_SIG_DFL;
551         userSignals &= ~sigmask(sig);
552         handler = SIG_DFL;
553         break;
554     default:
555         handlers[sig] = spi;
556         userSignals |= sigmask(sig);
557         handler = generic_handler;
558         break;
559     }
560
561     if (signal(sig, handler) < 0) {
562         if (previous_spi)
563           freeStablePointer(handlers[sig]);
564         sigsetmask(mask);
565         return STG_SIG_ERR;
566     }
567
568     sigsetmask(mask);
569     return previous_spi;
570 }
571
572 # endif    /* !POSIX */
573
574 #endif  /* PAR */
575
576 \end{code}