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