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