[project @ 2005-01-29 09:57:42 by stolz]
[ghc-hetmet.git] / ghc / 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
14 extern int stg_InstallConsoleEvent(int action, StgStablePtr *handler);
15
16 static BOOL WINAPI shutdown_handler(DWORD dwCtrlType);
17 static BOOL WINAPI generic_handler(DWORD dwCtrlType);
18
19 static rtsBool deliver_event = rtsTrue;
20 static StgInt console_handler = STG_SIG_DFL;
21
22 #define N_PENDING_EVENTS 16
23 StgInt stg_pending_events = 0;           /* number of undelivered events */
24 DWORD stg_pending_buf[N_PENDING_EVENTS]; /* their associated event numbers. */
25
26 /*
27  * Function: initUserSignals()
28  *
29  * Initialize the console handling substrate.
30  */
31 void
32 initUserSignals(void)
33 {
34     stg_pending_events = 0;
35     console_handler = STG_SIG_DFL;
36     return;
37 }
38
39 /*
40  * Function: shutdown_handler()
41  *
42  * Local function that performs the default handling of Ctrl+C kind
43  * events; gently shutting down the RTS
44  *
45  * To repeat Signals.c remark -- user code may choose to override the
46  * default handler. Which is fine, assuming they put back the default
47  * handler when/if they de-install the custom handler.
48  * 
49  */
50 static BOOL WINAPI shutdown_handler(DWORD dwCtrlType)
51 {
52     switch (dwCtrlType) {
53     
54     case CTRL_CLOSE_EVENT:
55         /* see generic_handler() comment re: this event */
56         return FALSE;
57     case CTRL_C_EVENT:
58     case CTRL_BREAK_EVENT:
59
60         // If we're already trying to interrupt the RTS, terminate with
61         // extreme prejudice.  So the first ^C tries to exit the program
62         // cleanly, and the second one just kills it.
63         if (interrupted) {
64             stg_exit(EXIT_INTERRUPTED);
65         } else {
66             interruptStgRts();
67             /* Cheesy pulsing of an event to wake up a waiting RTS thread, if any */
68             abandonRequestWait();
69             resetAbandonRequestWait();
70         }
71         return TRUE;
72
73         /* shutdown + logoff events are not handled here. */
74     default:
75         return FALSE;
76     }
77 }
78
79
80 /*
81  * Function: initDefaultHandlers()
82  *
83  * Install any default signal/console handlers. Currently we install a
84  * Ctrl+C handler that shuts down the RTS in an orderly manner.
85  */
86 void initDefaultHandlers(void)
87 {
88     if ( !SetConsoleCtrlHandler(shutdown_handler, TRUE) ) {
89         errorBelch("warning: failed to install default console handler");
90     }
91 }
92
93
94 /*
95  * Function: blockUserSignals()
96  *
97  * Temporarily block the delivery of further console events. Needed to
98  * avoid race conditions when GCing the stack of outstanding handlers or
99  * when emptying the stack by running the handlers.
100  * 
101  */
102 void
103 blockUserSignals(void)
104 {
105     deliver_event = rtsFalse;
106 }
107
108
109 /*
110  * Function: unblockUserSignals()
111  *
112  * The inverse of blockUserSignals(); re-enable the deliver of console events.
113  */
114 void
115 unblockUserSignals(void)
116 {
117     deliver_event = rtsTrue;
118 }
119
120
121 /*
122  * Function: awaitUserSignals()
123  *
124  * Wait for the next console event. Currently a NOP (returns immediately.)
125  */
126 void awaitUserSignals(void)
127 {
128     return;
129 }
130
131
132 /*
133  * Function: startSignalHandlers()
134  *
135  * Run the handlers associated with the stacked up console events. Console
136  * event delivery is blocked for the duration of this call.
137  */
138 void startSignalHandlers(void)
139 {
140     StgStablePtr handler;
141
142     if (console_handler < 0) {
143         return;
144     }
145     blockUserSignals();
146     
147     handler = deRefStablePtr((StgStablePtr)console_handler);
148     while (stg_pending_events > 0) {
149         stg_pending_events--;
150         scheduleThread(
151             createIOThread(RtsFlags.GcFlags.initialStkSize, 
152                            rts_apply((StgClosure *)handler,
153                                      rts_mkInt(stg_pending_buf[stg_pending_events]))));
154     }
155     unblockUserSignals();
156 }
157
158
159 /*
160  * Function: markSignalHandlers()
161  *
162  * Evacuate the handler stack. _Assumes_ that console event delivery
163  * has already been blocked.
164  */
165 void markSignalHandlers (evac_fn evac)
166 {
167     if (console_handler >= 0) {
168         StgPtr p = deRefStablePtr((StgStablePtr)console_handler);
169         evac((StgClosure**)&p);
170     }
171 }
172
173
174 /*
175  * Function: handleSignalsInThisThread()
176  * 
177  * Have current (OS) thread assume responsibility of handling console events/signals.
178  * Currently not used (by the console event handling code.)
179  */
180 void handleSignalsInThisThread(void)
181 {
182     return;
183 }
184
185 /* 
186  * Function: generic_handler()
187  *
188  * Local function which handles incoming console event (done in a sep OS thread),
189  * recording the event in stg_pending_events. 
190  */
191 static BOOL WINAPI generic_handler(DWORD dwCtrlType)
192 {
193     /* Ultra-simple -- up the counter + signal a switch. */
194     switch(dwCtrlType) {
195     case CTRL_CLOSE_EVENT:
196         /* Don't support the delivery of this event; if we
197          * indicate that we've handled it here and the Haskell handler
198          * doesn't take proper action (e.g., terminate the OS process),
199          * the user of the app will be unable to kill/close it. Not
200          * good, so disable the delivery for now.
201          */
202         return FALSE;
203     default:
204         if (!deliver_event) return TRUE;
205
206         if ( stg_pending_events < N_PENDING_EVENTS ) {
207             stg_pending_buf[stg_pending_events] = dwCtrlType;
208             stg_pending_events++;
209         }
210         /* Cheesy pulsing of an event to wake up a waiting RTS thread, if any */
211         abandonRequestWait();
212         resetAbandonRequestWait();
213         return TRUE;
214     }
215 }
216
217
218 /*
219  * Function: stg_InstallConsoleEvent()
220  *
221  * Install/remove a console event handler.
222  */
223 int
224 stg_InstallConsoleEvent(int action, StgStablePtr *handler)
225 {
226     StgInt previous_hdlr = console_handler;
227
228     switch (action) {
229     case STG_SIG_IGN:
230         console_handler = STG_SIG_IGN;
231         if ( !SetConsoleCtrlHandler(NULL, TRUE) ) {
232             errorBelch("warning: unable to ignore console events");
233         }
234         break;
235     case STG_SIG_DFL:
236         console_handler = STG_SIG_IGN;
237         if ( !SetConsoleCtrlHandler(NULL, FALSE) ) {
238             errorBelch("warning: unable to restore default console event handling");
239         }
240         break;
241     case STG_SIG_HAN:
242         console_handler = (StgInt)*handler;
243         if ( previous_hdlr < 0 ) {
244           /* Only install generic_handler() once */
245           if ( !SetConsoleCtrlHandler(generic_handler, TRUE) ) {
246             errorBelch("warning: unable to install console event handler");
247           }
248         }
249         break;
250     }
251     
252     if (previous_hdlr == STG_SIG_DFL || 
253         previous_hdlr == STG_SIG_IGN) {
254         return previous_hdlr;
255     } else {
256         *handler = (StgStablePtr)previous_hdlr;
257         return STG_SIG_HAN;
258     }
259 }