[project @ 1998-11-26 09:17:22 by sof]
[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 "config.h"
23
24 /* Treat nexttep3 and sunos4 alike. CaS */
25 #if defined(nextstep3_TARGET_OS)
26 # define NON_POSIX_SOURCE
27 #endif
28  
29 #if defined(sunos4_TARGET_OS)
30     /* The sigaction in SunOS 4.1.X does not grok SA_SIGINFO */
31 # define NON_POSIX_SOURCE
32 #endif
33
34 #if defined(freebsd_TARGET_OS) 
35 # define NON_POSIX_SOURCE
36 #endif
37
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
41 #endif
42
43 #if irix_TARGET_OS
44 /* SIGVTALRM not avail w/ POSIX_SOURCE, but worse things happen without */
45 /* SIGH: triple SIGH (WDP 95/07) */
46 # define SIGVTALRM 28
47 #endif
48
49 #include "rtsdefs.h"
50
51 #if defined(HAVE_SYS_TYPES_H)
52 # include <sys/types.h>
53 #endif
54
55         /* This is useful with the particular set of header files on my NeXT.
56          * CaS
57          */
58 #if defined(HAVE_SYS_SIGNAL_H)
59 # include <sys/signal.h>
60 #endif
61
62 #if defined(HAVE_SIGNAL_H)
63 # include <signal.h>
64 #endif
65
66 #if defined(linux_TARGET_OS) || defined(linuxaout_TARGET_OS)
67 /* to look *inside* sigcontext... 
68
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
74    was made).
75 */
76 # ifndef LINUX_VERSION_CODE
77 #  include <linux/version.h>
78 # endif
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>
83 # else
84 #  include <asm/sigcontext.h>
85 # endif
86
87 #endif
88
89 #if defined(HAVE_SIGINFO_H)
90     /* DEC OSF1 seems to need this explicitly.  Maybe others do as well? */
91 # include <siginfo.h>
92 #endif
93
94 #if defined(cygwin32_TARGET_OS)
95 # include <windows.h>
96 #endif
97
98 \end{code}
99
100 %************************************************************************
101 %*                                                                      *
102 \subsection{Stack-check by protected-memory-faulting}
103 %*                                                                      *
104 %************************************************************************
105
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
110 fault.
111
112 \begin{code}
113 #if STACK_CHECK_BY_PAGE_FAULT
114         /* NB: At the moment, this is always false on nextstep3. CaS. */
115
116 extern P_ stks_space;       /* Where the stacks live, from SMstacks.lc */
117 \end{code}
118
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.
122 Fun, eh?
123
124 \begin{code}
125 # if defined(sunos4_TARGET_OS) || defined(freebsd_TARGET_OS) \
126   || defined(linux_TARGET_OS)  || defined(linuxaout_TARGET_OS) \
127   || defined(aix_TARGET_OS)
128
129 static void
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)
137 #    else /* linux */
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>.
142         or not.
143
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!)
146      */
147 #     if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,51)
148            /* sigcontext_struct has been renamed to sigcontext */
149         struct sigcontext scp)
150 #     else
151         struct sigcontext_struct scp)
152 #     endif
153 #    endif 
154 #  endif
155 {
156     extern void StackOverflow(STG_NO_ARGS) STG_NORETURN;
157
158 #  if defined(linux_TARGET_OS)  || defined(linuxaout_TARGET_OS)
159     unsigned long addr = scp.cr2;
160     /* Magic info from Tommy Thorn! */
161 #  endif
162 #  if defined(aix_TARGET_OS)
163     caddr_t addr = scp->sc_jmpbuf.jmp_context.o_vaddr;
164     /* Magic guess by andre */
165 #  endif
166     if ( (char *)addr >= (char *)stks_space
167       && (char *)addr <  (char *)(stks_space + RTSflags.GcFlags.stksSize))
168         StackOverflow();
169
170     fflush(stdout);
171     fprintf(stderr, "Segmentation fault caught, address = %lx\n", (W_) addr);
172     abort();
173 }
174
175 int
176 install_segv_handler(void)
177 {
178 #if freebsd_TARGET_OS
179     /* FreeBSD seems to generate SIGBUS for stack overflows */
180     if (signal(SIGBUS, segv_handler) == SIG_ERR)
181         return -1;
182     if (signal(SIGSEGV, segv_handler) == SIG_ERR)
183         return -1;
184     return 0;
185 #else
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
189     */
190 #endif
191 }
192
193 # elif defined(irix6_TARGET_OS)
194
195 static void
196 segv_handler(int sig, siginfo_t *sip, void *dummy)
197 {
198     fflush(stdout);
199     if (sip == NULL) {
200         fprintf(stderr, "Segmentation fault caught, address unknown\n");
201     } else {
202         if (sip->si_addr >= (void *) stks_space
203           && sip->si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
204             StackOverflow();
205         fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
206     }
207     abort();
208 }
209
210 int
211 install_segv_handler(STG_NO_ARGS)
212 {
213     struct sigaction action;
214
215     action.sa_sigaction = segv_handler;
216     sigemptyset(&action.sa_mask);
217     action.sa_flags = SA_SIGINFO;
218
219     return sigaction(SIGSEGV, &action, NULL);
220 }
221
222 # elif defined(cygwin32_TARGET_OS)
223
224 /*
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.
229 */
230 static void
231 segv_handler(sig)
232  int sig;
233 {
234     CONTEXT context;
235     HANDLE hThread;
236     BOOL t;
237
238     context.ContextFlags = CONTEXT_CONTROL;
239     hThread = GetCurrentThread(); /* cannot fail */
240     t = GetThreadContext(hThread,&context);
241
242     fflush(stdout);
243     if (t == FALSE) {
244         fprintf(stderr, "Segmentation fault caught, address unknown\n");
245     } else {
246         void *si_addr = context.Eip; /* magic */
247         if (si_addr >= (void *) stks_space
248           && si_addr < (void *) (stks_space + RTSflags.GcFlags.stksSize))
249             StackOverflow();
250
251         fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_)si_addr);
252     }
253     abort();
254 }
255
256 int
257 install_segv_handler()
258 {
259     return (int) signal(SIGSEGV, segv_handler) == -1;
260 }
261
262 # else /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
263
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
267 #  endif
268
269 static void
270 segv_handler(int sig, siginfo_t *sip)
271   /* NB: the second "siginfo_t" argument is not really standard */
272 {
273     fflush(stdout);
274     if (sip == NULL) {
275         fprintf(stderr, "Segmentation fault caught, address unknown\n");
276     } else {
277         if (sip->si_addr >= (caddr_t) stks_space
278           && sip->si_addr < (caddr_t) (stks_space + RTSflags.GcFlags.stksSize))
279             StackOverflow();
280
281         fprintf(stderr, "Segmentation fault caught, address = %08lx\n", (W_) sip->si_addr);
282     }
283     abort();
284 }
285
286 int
287 install_segv_handler(STG_NO_ARGS)
288 {
289     struct sigaction action;
290
291     action.sa_handler = segv_handler;
292     sigemptyset(&action.sa_mask);
293     action.sa_flags = SA_SIGINFO;
294
295     return sigaction(SIGSEGV, &action, NULL);
296 }
297
298 # endif /* ! (cygwin32|irix6|sunos4|linux*|*bsd|aix) */
299
300 #endif  /* STACK_CHECK_BY_PAGE_FAULT */
301
302 \end{code}
303
304 %************************************************************************
305 %*                                                                      *
306 \subsection{Virtual-timer alarm (for profiling, etc.)}
307 %*                                                                      *
308 %************************************************************************
309
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
313 here.
314
315 \begin{code}
316 #if defined(PROFILING) || defined(CONCURRENT) /* && !defined(GRAN) */
317
318 # ifdef CONCURRENT
319
320 extern I_ delayTicks;
321
322 #  ifdef PAR
323 extern P_ CurrentTSO;
324 #  endif
325
326 /*
327  cygwin32 does not support VTALRM (sigh) - to do anything
328  sensible here we use the underlying Win32 calls.
329  (will this work??)
330 */
331 #   if defined(cygwin32_TARGET_OS)
332 /* windows.h already included */
333 static VOID CALLBACK 
334 vtalrm_handler(uID,uMsg,dwUser,dw1,dw2)
335 int uID;
336 unsigned int uMsg;
337 unsigned int dwUser;
338 unsigned int dw1;
339 unsigned int dw2;
340 #   else
341 static void
342 vtalrm_handler(int sig)
343 #   endif
344 {
345 /*
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.
349 */
350
351 #  if defined(PROFILING) || defined(PAR)
352     static I_ csTicks = 0, pTicks = 0;
353
354     if (time_profiling) {
355         if (++pTicks % RTSflags.CcFlags.profilerTicks == 0) {
356 #   if ! defined(PROFILING)
357             handle_tick_serial();
358 #   else
359             if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
360              || RTSflags.ProfFlags.doHeapProfile)
361                 handle_tick_serial();
362             else
363                 handle_tick_noserial();
364 #   endif
365         }
366         if (++csTicks % RTSflags.CcFlags.ctxtSwitchTicks != 0)
367             return;
368     }
369 #  endif
370
371        /*
372          Handling a tick for threads blocked waiting for file
373          descriptor I/O or time.
374
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).
381
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)
384
385          4/96 SOF
386        */
387
388     if (delayTicks != 0) /* delayTicks>0 => don't handle timer expiry (in GC) */
389        delayTicks++;
390     else if (WaitingThreadsHd != PrelBase_Z91Z93_closure)
391              AwaitEvent(RTSflags.ConcFlags.ctxtSwitchTime);
392
393 #  ifdef PAR
394     if (PendingSparksTl[REQUIRED_POOL] == PendingSparksLim[REQUIRED_POOL] ||
395       PendingSparksTl[ADVISORY_POOL] == PendingSparksLim[ADVISORY_POOL]) {
396         PruneSparks();
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; 
404         }
405     }
406
407     if (CurrentTSO != NULL ||
408 #  else
409     if (RunnableThreadsHd != PrelBase_Z91Z93_closure ||
410 #  endif
411       PendingSparksHd[REQUIRED_POOL] < PendingSparksTl[REQUIRED_POOL] ||
412       PendingSparksHd[ADVISORY_POOL] < PendingSparksTl[ADVISORY_POOL]) {
413         /* ToDo: anything else for GRAN? WDP */
414         context_switch = 1;
415     }
416 }
417
418 # endif
419
420
421 #if defined(cygwin32_TARGET_OS) /* really just Win32 */
422 /* windows.h already included for the segv_handling above */
423
424 I_ vtalrm_id;
425 TIMECALLBACK *vtalrm_cback;
426
427 #ifndef CONCURRENT
428 void (*tick_handle)(STG_NO_ARGS);
429
430 static VOID CALLBACK 
431 tick_handler(uID,uMsg,dwUser,dw1,dw2)
432 int uID;
433 unsigned int uMsg;
434 unsigned int dwUser;
435 unsigned int dw1;
436 unsigned int dw2;
437 {
438  (*tick_handle)();
439 }
440 #endif
441
442 int install_vtalrm_handler()
443 {
444 #  ifdef CONCURRENT
445     vtalrm_cback = vtalrm_handler;
446 #  else
447      /*
448         Only turn on ticking 
449      */
450     vtalrm_cback = tick_handler;
451     if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
452      || RTSflags.ProfFlags.doHeapProfile)
453         tick_handle = handle_tick_serial;
454     else
455         tick_handle = handle_tick_noserial;
456 #  endif
457     return (int)0;
458 }  
459
460 void
461 blockVtAlrmSignal(STG_NO_ARGS)
462 {
463  timeKillEvent(vtalrm_id);
464 }
465
466 void
467 unblockVtAlrmSignal(STG_NO_ARGS)
468 {
469 #ifdef CONCURRENT
470  timeSetEvent(RTSflags.ConcFlags.ctxtSwitchTime,5,vtalrm_cback,NULL,TIME_PERIODIC);
471 #else
472  timeSetEvent(RTSflags.CcFlags.msecsPerTick,5,vtalrm_cback,NULL,TIME_PERIODIC);
473 #endif
474 }
475
476 #elif defined(sunos4_TARGET_OS)
477
478 int
479 install_vtalrm_handler(void)
480 {
481     void (*old)();
482
483 #  ifdef CONCURRENT
484     old = signal(SIGVTALRM, vtalrm_handler);
485 #  else
486     if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
487      || RTSflags.ProfFlags.doHeapProfile)
488         old = signal(SIGVTALRM, handle_tick_serial);
489     else
490         old = signal(SIGVTALRM, handle_tick_noserial);
491 #  endif
492     return ((int) old == SIG_ERR);
493 }
494
495 static int vtalrm_mask;
496
497 void
498 blockVtAlrmSignal(STG_NO_ARGS)
499 {
500     vtalrm_mask = sigblock(sigmask(SIGVTALRM));
501 }
502
503 void
504 unblockVtAlrmSignal(STG_NO_ARGS)
505 {
506     (void) sigsetmask(vtalrm_mask);
507 }
508
509 # else  /* Not SunOS 4 */
510
511 int
512 install_vtalrm_handler(STG_NO_ARGS)
513 {
514     struct sigaction action;
515
516 #  ifdef CONCURRENT
517     action.sa_handler = vtalrm_handler;
518 #  else
519     if (RTSflags.CcFlags.doCostCentres >= COST_CENTRES_VERBOSE
520      || RTSflags.ProfFlags.doHeapProfile)
521         action.sa_handler = handle_tick_serial;
522     else
523         action.sa_handler = handle_tick_noserial;
524 #  endif
525
526     sigemptyset(&action.sa_mask);
527     action.sa_flags = 0;
528
529     return sigaction(SIGVTALRM, &action, NULL);
530 }
531
532 void
533 blockVtAlrmSignal(STG_NO_ARGS)
534 {
535     sigset_t signals;
536     
537     sigemptyset(&signals);
538     sigaddset(&signals, SIGVTALRM);
539
540     (void) sigprocmask(SIG_BLOCK, &signals, NULL);
541 }
542
543 void
544 unblockVtAlrmSignal(STG_NO_ARGS)
545 {
546     sigset_t signals;
547     
548     sigemptyset(&signals);
549     sigaddset(&signals, SIGVTALRM);
550
551     (void) sigprocmask(SIG_UNBLOCK, &signals, NULL);
552 }
553
554 # endif /* ! SunOS 4 */
555
556 #endif /* PROFILING || CONCURRENT (but not GRAN) */
557
558 \end{code}
559
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.
563
564 \begin{code}
565
566 #if defined(PAR) /* || defined(GRAN) */
567
568 void
569 blockUserSignals(void)
570 {
571     return;
572 }
573
574 void
575 unblockUserSignals(void)
576 {
577     return;
578 }
579
580 I_ 
581 # ifdef _POSIX_SOURCE
582 sig_install(sig, spi, mask)
583   sigset_t *mask;
584 # else
585   sig_install(sig, spi)
586 # endif
587   I_ sig;
588   I_ spi;
589 {
590     fflush(stdout);
591     fprintf(stderr,"No signal handling support in a parallel implementation.\n");
592     EXIT(EXIT_FAILURE);
593 }
594
595 #else   /* !PAR */
596
597 # include <setjmp.h>
598
599 extern StgPtr deRefStablePointer PROTO((StgStablePtr));
600 extern void freeStablePointer PROTO((I_));
601 extern jmp_buf restart_main;
602
603 static I_ *handlers = NULL; /* Dynamically grown array of signal handlers */
604 static I_ nHandlers = 0;    /* Size of handlers array */
605
606 static void
607 more_handlers(I_ sig)
608 {
609     I_ i;
610
611     if (sig < nHandlers)
612         return;
613
614     if (handlers == NULL)
615         handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
616     else
617         handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
618
619     if (handlers == NULL) {
620         fflush(stdout);
621         fprintf(stderr, "VM exhausted (in more_handlers)\n");
622         EXIT(EXIT_FAILURE);
623     }
624     for(i = nHandlers; i <= sig; i++)
625         /* Fill in the new slots with default actions */
626         handlers[i] = STG_SIG_DFL;
627
628     nHandlers = sig + 1;
629 }
630
631 I_ nocldstop = 0;
632
633 # ifdef _POSIX_SOURCE
634
635 static void
636 generic_handler(int sig)
637 {
638     sigset_t signals;
639
640     SAVE_Hp = SAVE_HpLim;       /* Just to be safe */
641     if (! initStacks(&StorageMgrInfo)) {
642         fflush(stdout);
643         fprintf(stderr, "initStacks failed!\n");
644         EXIT(EXIT_FAILURE);
645     }
646     TopClosure = deRefStablePointer(handlers[sig]);
647     sigemptyset(&signals);
648     sigaddset(&signals, sig);
649     sigprocmask(SIG_UNBLOCK, &signals, NULL);
650     longjmp(restart_main, sig);
651 }
652
653 static sigset_t userSignals;
654 static sigset_t savedSignals;
655
656 void
657 initUserSignals(void)
658 {
659     sigemptyset(&userSignals);
660 }
661
662 void
663 blockUserSignals(void)
664 {
665     sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
666 }
667
668 void
669 unblockUserSignals(void)
670 {
671     sigprocmask(SIG_SETMASK, &savedSignals, NULL);
672 }
673
674
675 I_ 
676 sig_install(sig, spi, mask)
677   I_ sig;
678   I_ spi;
679   sigset_t *mask;
680 {
681     sigset_t signals;
682     struct sigaction action;
683     I_ previous_spi;
684
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))
689         return STG_SIG_ERR;
690
691     more_handlers(sig);
692
693     previous_spi = handlers[sig];
694
695     switch(spi) {
696     case STG_SIG_IGN:
697         handlers[sig] = STG_SIG_IGN;
698         sigdelset(&userSignals, sig);
699         action.sa_handler = SIG_IGN;
700         break;
701         
702     case STG_SIG_DFL:
703         handlers[sig] = STG_SIG_DFL;
704         sigdelset(&userSignals, sig);
705         action.sa_handler = SIG_DFL;
706         break;
707     default:
708         handlers[sig] = spi;
709         sigaddset(&userSignals, sig);
710         action.sa_handler = generic_handler;
711         break;
712     }
713
714     if (mask != NULL)
715         action.sa_mask = *mask;
716     else
717         sigemptyset(&action.sa_mask);
718
719     action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
720
721     if (sigaction(sig, &action, NULL) || sigprocmask(SIG_UNBLOCK, &signals, NULL)) {
722         if (previous_spi)
723           freeStablePointer(handlers[sig]);
724         return STG_SIG_ERR;
725     }
726
727     return previous_spi;
728 }
729
730 # else  /* !POSIX */
731
732 static void
733 generic_handler(sig)
734 {
735     SAVE_Hp = SAVE_HpLim;       /* Just to be safe */
736     if (! initStacks(&StorageMgrInfo)) {
737         fflush(stdout);
738         fprintf(stderr, "initStacks failed!\n");
739         EXIT(EXIT_FAILURE);
740     }
741     TopClosure = deRefStablePointer(handlers[sig]);
742     sigsetmask(0);
743     longjmp(restart_main, sig);
744 }
745
746 static int userSignals;
747 static int savedSignals;
748
749 void
750 initUserSignals(void)
751 {
752     userSignals = 0;
753 }
754
755 void
756 blockUserSignals(void)
757 {
758     savedSignals = sigsetmask(userSignals);
759 }
760
761 void
762 unblockUserSignals(void)
763 {
764     sigsetmask(savedSignals);
765 }
766
767 I_ 
768 sig_install(sig, spi)
769   I_ sig;
770   I_ spi;
771 {
772     I_ previous_spi;
773     int mask;
774     void (*handler)(int);
775
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)
779         return STG_SIG_ERR;
780
781     mask = sigblock(mask);
782
783     more_handlers(sig);
784
785     previous_spi = handlers[sig];
786
787     switch(spi) {
788     case STG_SIG_IGN:
789         handlers[sig] = STG_SIG_IGN;
790         userSignals &= ~sigmask(sig);
791         handler = SIG_IGN;
792         break;
793         
794     case STG_SIG_DFL:
795         handlers[sig] = STG_SIG_DFL;
796         userSignals &= ~sigmask(sig);
797         handler = SIG_DFL;
798         break;
799     default:
800         handlers[sig] = spi;
801         userSignals |= sigmask(sig);
802         handler = generic_handler;
803         break;
804     }
805
806     if (signal(sig, handler) < 0) {
807         if (previous_spi)
808           freeStablePointer(handlers[sig]);
809         sigsetmask(mask);
810         return STG_SIG_ERR;
811     }
812
813     sigsetmask(mask);
814     return previous_spi;
815 }
816
817 # endif    /* !POSIX */
818
819 #endif  /* PAR */
820
821 \end{code}