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