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