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