4220f29145e0c7b3a3acef2e2cfe900f25b6e880
[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_C_EVENT:
54     case CTRL_BREAK_EVENT:
55     case CTRL_CLOSE_EVENT:
56
57         // If we're already trying to interrupt the RTS, terminate with
58         // extreme prejudice.  So the first ^C tries to exit the program
59         // cleanly, and the second one just kills it.
60         if (interrupted) {
61             stg_exit(EXIT_INTERRUPTED);
62         } else {
63             interruptStgRts();
64         }
65         return TRUE;
66
67         /* shutdown + logoff events are not handled here. */
68     default:
69         return FALSE;
70     }
71 }
72
73
74 /*
75  * Function: initDefaultHandlers()
76  *
77  * Install any default signal/console handlers. Currently we install a
78  * Ctrl+C handler that shuts down the RTS in an orderly manner.
79  */
80 void initDefaultHandlers(void)
81 {
82     if ( !SetConsoleCtrlHandler(shutdown_handler, TRUE) ) {
83         errorBelch("warning: failed to install default console handler");
84     }
85 }
86
87
88 /*
89  * Function: blockUserSignals()
90  *
91  * Temporarily block the delivery of further console events. Needed to
92  * avoid race conditions when GCing the stack of outstanding handlers or
93  * when emptying the stack by running the handlers.
94  * 
95  */
96 void
97 blockUserSignals(void)
98 {
99     deliver_event = rtsFalse;
100 }
101
102
103 /*
104  * Function: unblockUserSignals()
105  *
106  * The inverse of blockUserSignals(); re-enable the deliver of console events.
107  */
108 void
109 unblockUserSignals(void)
110 {
111     deliver_event = rtsTrue;
112 }
113
114
115 /*
116  * Function: awaitUserSignals()
117  *
118  * Wait for the next console event. Currently a NOP (returns immediately.)
119  */
120 void awaitUserSignals(void)
121 {
122     return;
123 }
124
125
126 /*
127  * Function: startSignalHandlers()
128  *
129  * Run the handlers associated with the stacked up console events. Console
130  * event delivery is blocked for the duration of this call.
131  */
132 void startSignalHandlers(void)
133 {
134     StgStablePtr handler;
135
136     if (console_handler < 0) {
137         return;
138     }
139     blockUserSignals();
140     
141     handler = deRefStablePtr((StgStablePtr)console_handler);
142     while (stg_pending_events > 0) {
143         stg_pending_events--;
144         scheduleThread(
145             createIOThread(RtsFlags.GcFlags.initialStkSize, 
146                            rts_apply((StgClosure *)handler,
147                                      rts_mkInt(stg_pending_buf[stg_pending_events]))));
148     }
149     unblockUserSignals();
150 }
151
152
153 /*
154  * Function: markSignalHandlers()
155  *
156  * Evacuate the handler stack. _Assumes_ that console event delivery
157  * has already been blocked.
158  */
159 void markSignalHandlers (evac_fn evac)
160 {
161     if (console_handler >= 0) {
162         StgPtr p = deRefStablePtr((StgStablePtr)console_handler);
163         evac((StgClosure**)&p);
164     }
165 }
166
167
168 /*
169  * Function: handleSignalsInThisThread()
170  * 
171  * Have current (OS) thread assume responsibility of handling console events/signals.
172  * Currently not used (by the console event handling code.)
173  */
174 void handleSignalsInThisThread(void)
175 {
176     return;
177 }
178
179 /* 
180  * Function: generic_handler()
181  *
182  * Local function which handles incoming console event (done in a sep OS thread),
183  * recording the event in stg_pending_events. 
184  */
185 static BOOL WINAPI generic_handler(DWORD dwCtrlType)
186 {
187     /* Ultra-simple -- up the counter + signal a switch. */
188     if ( stg_pending_events < N_PENDING_EVENTS ) {
189         stg_pending_buf[stg_pending_events] = dwCtrlType;
190         stg_pending_events++;
191     }
192     context_switch = 1;
193     return TRUE;
194 }
195
196
197 /*
198  * Function: stg_InstallConsoleEvent()
199  *
200  * Install/remove a console event handler.
201  */
202 int
203 stg_InstallConsoleEvent(int action, StgStablePtr *handler)
204 {
205     StgInt previous_hdlr = console_handler;
206
207     switch (action) {
208     case STG_SIG_IGN:
209         console_handler = STG_SIG_IGN;
210         if ( !SetConsoleCtrlHandler(NULL, TRUE) ) {
211             errorBelch("warning: unable to ignore console events");
212         }
213         break;
214     case STG_SIG_DFL:
215         console_handler = STG_SIG_IGN;
216         if ( !SetConsoleCtrlHandler(NULL, FALSE) ) {
217             errorBelch("warning: unable to restore default console event handling");
218         }
219         break;
220     case STG_SIG_HAN:
221         console_handler = (StgInt)*handler;
222         if ( previous_hdlr < 0 ) {
223           /* Only install generic_handler() once */
224           if ( !SetConsoleCtrlHandler(generic_handler, TRUE) ) {
225             errorBelch("warning: unable to install console event handler");
226           }
227         }
228         break;
229     }
230     
231     if (previous_hdlr == STG_SIG_DFL || 
232         previous_hdlr == STG_SIG_IGN) {
233         return previous_hdlr;
234     } else {
235         *handler = (StgStablePtr)previous_hdlr;
236         return STG_SIG_HAN;
237     }
238 }