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