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