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