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