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