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