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