[project @ 1999-02-05 16:02:18 by simonm]
[ghc-hetmet.git] / ghc / rts / Signals.c
1 /* -----------------------------------------------------------------------------
2  * $Id: Signals.c,v 1.4 1999/02/05 16:02:54 simonm Exp $
3  *
4  * (c) The GHC Team, 1998-1999
5  *
6  * Signal processing / handling.
7  *
8  * ---------------------------------------------------------------------------*/
9
10 #include "Rts.h"
11 #include "SchedAPI.h"
12 #include "Signals.h"
13 #include "RtsUtils.h"
14 #include "RtsFlags.h"
15 #include "StablePriv.h"
16
17 #ifndef PAR
18
19 static StgInt *handlers = NULL; /* Dynamically grown array of signal handlers */
20 static StgInt nHandlers = 0;    /* Size of handlers array */
21
22 #define N_PENDING_HANDLERS 16
23
24 StgPtr pending_handler_buf[N_PENDING_HANDLERS];
25 StgPtr *next_pending_handler = pending_handler_buf;
26
27 StgInt nocldstop = 0;
28
29 /* -----------------------------------------------------------------------------
30    Allocate/resize the table of signal handlers.
31    -------------------------------------------------------------------------- */
32
33 static void
34 more_handlers(I_ sig)
35 {
36     I_ i;
37
38     if (sig < nHandlers)
39         return;
40
41     if (handlers == NULL)
42         handlers = (I_ *) malloc((sig + 1) * sizeof(I_));
43     else
44         handlers = (I_ *) realloc(handlers, (sig + 1) * sizeof(I_));
45
46     if (handlers == NULL) {
47         fflush(stdout);
48         fprintf(stderr, "VM exhausted (in more_handlers)\n");
49         exit(EXIT_FAILURE);
50     }
51     for(i = nHandlers; i <= sig; i++)
52         /* Fill in the new slots with default actions */
53         handlers[i] = STG_SIG_DFL;
54
55     nHandlers = sig + 1;
56 }
57
58 /* -----------------------------------------------------------------------------
59    Low-level signal handler
60
61    Places the requested handler on a stack of pending handlers to be
62    started up at the next context switch.
63    -------------------------------------------------------------------------- */
64
65 static void
66 generic_handler(int sig)
67 {
68     sigset_t signals;
69
70     /* Can't call allocate from here.  Probably can't call malloc
71        either.  However, we have to schedule a new thread somehow.
72
73        It's probably ok to request a context switch and allow the
74        scheduler to  start the handler thread, but how to we
75        communicate this to the scheduler?
76
77        We need some kind of locking, but with low overhead (i.e. no
78        blocking signals every time around the scheduler).
79        
80        Signal Handlers are atomic (i.e. they can't be interrupted), and
81        we can make use of this.  We just need to make sure the
82        critical section of the scheduler can't be interrupted - the
83        only way to do this is to block signals.  However, we can lower
84        the overhead by only blocking signals when there are any
85        handlers to run, i.e. the set of pending handlers is
86        non-empty.
87     */
88        
89     /* We use a stack to store the pending signals.  We can't
90        dynamically grow this since we can't allocate any memory from
91        within a signal handler.
92
93        Hence unfortunately we have to bomb out if the buffer
94        overflows.  It might be acceptable to carry on in certain
95        circumstances, depending on the signal.  
96     */
97
98     *next_pending_handler++ = deRefStablePtr(handlers[sig]);
99
100     /* stack full? */
101     if (next_pending_handler == &pending_handler_buf[N_PENDING_HANDLERS]) {
102       barf("too many pending signals");
103     }
104     
105     /* re-establish the signal handler, and carry on */
106     sigemptyset(&signals);
107     sigaddset(&signals, sig);
108     sigprocmask(SIG_UNBLOCK, &signals, NULL);
109 }
110
111 /* -----------------------------------------------------------------------------
112    Blocking/Unblocking of the user signals
113    -------------------------------------------------------------------------- */
114
115 static sigset_t userSignals;
116 static sigset_t savedSignals;
117
118 void
119 initUserSignals(void)
120 {
121     sigemptyset(&userSignals);
122 }
123
124 void
125 blockUserSignals(void)
126 {
127     sigprocmask(SIG_SETMASK, &userSignals, &savedSignals);
128 }
129
130 void
131 unblockUserSignals(void)
132 {
133     sigprocmask(SIG_SETMASK, &savedSignals, NULL);
134 }
135
136
137 /* -----------------------------------------------------------------------------
138    Install a Haskell signal handler.
139    -------------------------------------------------------------------------- */
140
141 StgInt 
142 sig_install(StgInt sig, StgInt spi, StgStablePtr handler, sigset_t *mask)
143 {
144     sigset_t signals;
145     struct sigaction action;
146     StgInt previous_spi;
147
148     /* Block the signal until we figure out what to do */
149     /* Count on this to fail if the signal number is invalid */
150     if(sig < 0 || sigemptyset(&signals) || sigaddset(&signals, sig) ||
151        sigprocmask(SIG_BLOCK, &signals, NULL))
152       return STG_SIG_ERR;
153
154     more_handlers(sig);
155
156     previous_spi = handlers[sig];
157
158     switch(spi) {
159     case STG_SIG_IGN:
160         handlers[sig] = STG_SIG_IGN;
161         sigdelset(&userSignals, sig);
162         action.sa_handler = SIG_IGN;
163         break;
164         
165     case STG_SIG_DFL:
166         handlers[sig] = STG_SIG_DFL;
167         sigdelset(&userSignals, sig);
168         action.sa_handler = SIG_DFL;
169         break;
170     case STG_SIG_HAN:
171         handlers[sig] = (I_)handler;
172         sigaddset(&userSignals, sig);
173         action.sa_handler = generic_handler;
174         break;
175     default:
176         barf("sig_install: bad spi");
177     }
178
179     if (mask != 0)
180         action.sa_mask = *mask;
181     else
182         sigemptyset(&action.sa_mask);
183
184     action.sa_flags = sig == SIGCHLD && nocldstop ? SA_NOCLDSTOP : 0;
185
186     if (sigaction(sig, &action, NULL) || 
187         sigprocmask(SIG_UNBLOCK, &signals, NULL)) 
188     {
189       /* need to return an error code, so avoid a stable pointer leak
190        * by freeing the previous handler if there was one.
191        */        
192       if (previous_spi >= 0) {
193           freeStablePtr(handlers[sig]);
194       }
195       return STG_SIG_ERR;
196     }
197
198     return previous_spi;
199 }
200
201 /* -----------------------------------------------------------------------------
202    Creating new threads for the pending signal handlers.
203    -------------------------------------------------------------------------- */
204
205 void
206 start_signal_handlers(void)
207 {
208   blockUserSignals();
209   
210   while (next_pending_handler != pending_handler_buf) {
211
212     next_pending_handler--;
213
214     /* create*Thread  puts the thread on the head of the runnable
215      * queue, hence it will be run next.  Poor man's priority
216      * scheduling.
217      */
218     createIOThread(RtsFlags.GcFlags.initialStkSize, 
219                    (StgClosure *) *next_pending_handler);
220   }
221
222   unblockUserSignals();
223 }
224
225 #else /* PAR */
226 StgInt 
227 sig_install(StgInt sig, StgInt spi, StgStablePtr handler, sigset_t *mask)
228 {
229     fflush(stdout);
230     fprintf(stderr,
231             "No signal handling support in a parallel implementation.\n");
232     exit(EXIT_FAILURE);
233 }
234
235 void
236 start_signal_handlers(void)
237 {
238 }
239 #endif