Massive patch for the first months work adding System FC to GHC #35
[ghc-hetmet.git] / rts / win32 / ConsoleHandler.c
1 /*
2  * Console control handler support.
3  *
4  */
5 #include "Rts.h"
6 #include <windows.h>
7 #include "ConsoleHandler.h"
8 #include "SchedAPI.h"
9 #include "Schedule.h"
10 #include "RtsUtils.h"
11 #include "RtsFlags.h"
12 #include "AsyncIO.h"
13 #include "RtsSignals.h"
14
15 extern int stg_InstallConsoleEvent(int action, StgStablePtr *handler);
16
17 static BOOL WINAPI shutdown_handler(DWORD dwCtrlType);
18 static BOOL WINAPI generic_handler(DWORD dwCtrlType);
19
20 static rtsBool deliver_event = rtsTrue;
21 static StgInt console_handler = STG_SIG_DFL;
22
23 static HANDLE hConsoleEvent = INVALID_HANDLE_VALUE;
24
25 #define N_PENDING_EVENTS 16
26 StgInt stg_pending_events = 0;           /* number of undelivered events */
27 DWORD stg_pending_buf[N_PENDING_EVENTS]; /* their associated event numbers. */
28
29 /*
30  * Function: initUserSignals()
31  *
32  * Initialize the console handling substrate.
33  */
34 void
35 initUserSignals(void)
36 {
37     stg_pending_events = 0;
38     console_handler = STG_SIG_DFL;
39     if (hConsoleEvent == INVALID_HANDLE_VALUE) {
40         hConsoleEvent = 
41             CreateEvent ( NULL,  /* default security attributes */
42                           TRUE,  /* manual-reset event */
43                           FALSE, /* initially non-signalled */
44                           NULL); /* no name */
45     }
46     return;
47 }
48
49 void
50 finiUserSignals(void)
51 {
52     if (hConsoleEvent != INVALID_HANDLE_VALUE) {
53         CloseHandle(hConsoleEvent);
54     }
55 }
56
57 /*
58  * Function: shutdown_handler()
59  *
60  * Local function that performs the default handling of Ctrl+C kind
61  * events; gently shutting down the RTS
62  *
63  * To repeat Signals.c remark -- user code may choose to override the
64  * default handler. Which is fine, assuming they put back the default
65  * handler when/if they de-install the custom handler.
66  * 
67  */
68 static BOOL WINAPI shutdown_handler(DWORD dwCtrlType)
69 {
70     switch (dwCtrlType) {
71     
72     case CTRL_CLOSE_EVENT:
73         /* see generic_handler() comment re: this event */
74         return FALSE;
75     case CTRL_C_EVENT:
76     case CTRL_BREAK_EVENT:
77
78         // If we're already trying to interrupt the RTS, terminate with
79         // extreme prejudice.  So the first ^C tries to exit the program
80         // cleanly, and the second one just kills it.
81         if (sched_state >= SCHED_INTERRUPTING) {
82             stg_exit(EXIT_INTERRUPTED);
83         } else {
84             interruptStgRts();
85             /* Cheesy pulsing of an event to wake up a waiting RTS thread, if any */
86             abandonRequestWait();
87             resetAbandonRequestWait();
88         }
89         return TRUE;
90
91         /* shutdown + logoff events are not handled here. */
92     default:
93         return FALSE;
94     }
95 }
96
97
98 /*
99  * Function: initDefaultHandlers()
100  *
101  * Install any default signal/console handlers. Currently we install a
102  * Ctrl+C handler that shuts down the RTS in an orderly manner.
103  */
104 void initDefaultHandlers(void)
105 {
106     if ( !SetConsoleCtrlHandler(shutdown_handler, TRUE) ) {
107         errorBelch("warning: failed to install default console handler");
108     }
109 }
110
111
112 /*
113  * Function: blockUserSignals()
114  *
115  * Temporarily block the delivery of further console events. Needed to
116  * avoid race conditions when GCing the stack of outstanding handlers or
117  * when emptying the stack by running the handlers.
118  * 
119  */
120 void
121 blockUserSignals(void)
122 {
123     deliver_event = rtsFalse;
124 }
125
126
127 /*
128  * Function: unblockUserSignals()
129  *
130  * The inverse of blockUserSignals(); re-enable the deliver of console events.
131  */
132 void
133 unblockUserSignals(void)
134 {
135     deliver_event = rtsTrue;
136 }
137
138
139 /*
140  * Function: awaitUserSignals()
141  *
142  * Wait for the next console event. Currently a NOP (returns immediately.)
143  */
144 void awaitUserSignals(void)
145 {
146     return;
147 }
148
149
150 /*
151  * Function: startSignalHandlers()
152  *
153  * Run the handlers associated with the stacked up console events. Console
154  * event delivery is blocked for the duration of this call.
155  */
156 void startSignalHandlers(Capability *cap)
157 {
158     StgStablePtr handler;
159
160     if (console_handler < 0) {
161         return;
162     }
163
164     blockUserSignals();
165     ACQUIRE_LOCK(&sched_mutex);
166     
167     handler = deRefStablePtr((StgStablePtr)console_handler);
168     while (stg_pending_events > 0) {
169         stg_pending_events--;
170         scheduleThread(cap,
171             createIOThread(cap,
172                            RtsFlags.GcFlags.initialStkSize, 
173                            rts_apply(cap,
174                                      (StgClosure *)handler,
175                                      rts_mkInt(cap,
176                                                stg_pending_buf[stg_pending_events]))));
177     }
178     
179     RELEASE_LOCK(&sched_mutex);
180     unblockUserSignals();
181 }
182
183 /*
184  * Function: markSignalHandlers()
185  *
186  * Evacuate the handler stack. _Assumes_ that console event delivery
187  * has already been blocked.
188  */
189 void markSignalHandlers (evac_fn evac)
190 {
191     // nothing to mark; the console handler is a StablePtr which is
192     // already treated as a root by the GC.
193 }
194
195
196 /* 
197  * Function: generic_handler()
198  *
199  * Local function which handles incoming console event (done in a sep OS thread),
200  * recording the event in stg_pending_events. 
201  */
202 static BOOL WINAPI generic_handler(DWORD dwCtrlType)
203 {
204     ACQUIRE_LOCK(&sched_mutex);
205
206     /* Ultra-simple -- up the counter + signal a switch. */
207     switch(dwCtrlType) {
208     case CTRL_CLOSE_EVENT:
209         /* Don't support the delivery of this event; if we
210          * indicate that we've handled it here and the Haskell handler
211          * doesn't take proper action (e.g., terminate the OS process),
212          * the user of the app will be unable to kill/close it. Not
213          * good, so disable the delivery for now.
214          */
215         return FALSE;
216     default:
217         if (!deliver_event) return TRUE;
218
219         if ( stg_pending_events < N_PENDING_EVENTS ) {
220             stg_pending_buf[stg_pending_events] = dwCtrlType;
221             stg_pending_events++;
222         }
223         /* Cheesy pulsing of an event to wake up a waiting RTS thread, if any */
224         abandonRequestWait();
225         resetAbandonRequestWait();
226         return TRUE;
227     }
228
229     RELEASE_LOCK(&sched_mutex);
230 }
231
232
233 /*
234  * Function: rts_InstallConsoleEvent()
235  *
236  * Install/remove a console event handler.
237  */
238 int
239 rts_InstallConsoleEvent(int action, StgStablePtr *handler)
240 {
241     StgInt previous_hdlr = console_handler;
242
243     switch (action) {
244     case STG_SIG_IGN:
245         console_handler = STG_SIG_IGN;
246         if ( !SetConsoleCtrlHandler(NULL, TRUE) ) {
247             errorBelch("warning: unable to ignore console events");
248         }
249         break;
250     case STG_SIG_DFL:
251         console_handler = STG_SIG_IGN;
252         if ( !SetConsoleCtrlHandler(NULL, FALSE) ) {
253             errorBelch("warning: unable to restore default console event handling");
254         }
255         break;
256     case STG_SIG_HAN:
257         console_handler = (StgInt)*handler;
258         if ( previous_hdlr < 0 ) {
259           /* Only install generic_handler() once */
260           if ( !SetConsoleCtrlHandler(generic_handler, TRUE) ) {
261             errorBelch("warning: unable to install console event handler");
262           }
263         }
264         break;
265     }
266     
267     if (previous_hdlr == STG_SIG_DFL || 
268         previous_hdlr == STG_SIG_IGN) {
269         return previous_hdlr;
270     } else {
271         *handler = (StgStablePtr)previous_hdlr;
272         return STG_SIG_HAN;
273     }
274 }
275
276 /*
277  * Function: rts_HandledConsoleEvent()
278  *
279  * Signal that a Haskell console event handler has completed its run.
280  * The explicit notification that a Haskell handler has completed is 
281  * required to better handle the delivery of Ctrl-C/Break events whilst
282  * an async worker thread is handling a read request on stdin. The 
283  * Win32 console implementation will abort such a read request when Ctrl-C
284  * is delivered. That leaves the worker thread in a bind: should it 
285  * abandon the request (the Haskell thread reading from stdin has been 
286  * thrown an exception to signal the delivery of Ctrl-C & hence have 
287  * aborted the I/O request) or simply ignore the aborted read and retry?
288  * (the Haskell thread reading from stdin isn't concerned with the
289  * delivery and handling of Ctrl-C.) With both scenarios being
290  * possible, the worker thread needs to be told -- that is, did the
291  * console event handler cause the IO request to be abandoned? 
292  *
293  */
294 void
295 rts_ConsoleHandlerDone(int ev)
296 {
297     if ( (DWORD)ev == CTRL_BREAK_EVENT ||
298          (DWORD)ev == CTRL_C_EVENT ) {
299         /* only these two cause stdin system calls to abort.. */
300         SetEvent(hConsoleEvent); /* event is manual-reset */
301         Sleep(0); /* yield */
302         ResetEvent(hConsoleEvent); /* turn it back off again */
303     }
304 }
305
306 /*
307  * Function: rts_waitConsoleHandlerCompletion()
308  *
309  * Esoteric entry point used by worker thread that got woken
310  * up as part Ctrl-C delivery.
311  */
312 int
313 rts_waitConsoleHandlerCompletion()
314 {
315     /* As long as the worker doesn't need to do a multiple wait,
316      * let's keep this HANDLE private to this 'module'.
317      */
318     return (WaitForSingleObject(hConsoleEvent, INFINITE) == WAIT_OBJECT_0);
319 }