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