2 * Console control handler support.
7 #include "ConsoleHandler.h"
13 #include "RtsSignals.h"
15 extern int stg_InstallConsoleEvent(int action, StgStablePtr *handler);
17 static BOOL WINAPI shutdown_handler(DWORD dwCtrlType);
18 static BOOL WINAPI generic_handler(DWORD dwCtrlType);
20 static rtsBool deliver_event = rtsTrue;
21 StgInt console_handler = STG_SIG_DFL;
23 #if !defined(THREADED_RTS)
25 static HANDLE hConsoleEvent = INVALID_HANDLE_VALUE;
27 #define N_PENDING_EVENTS 16
28 StgInt stg_pending_events = 0; /* number of undelivered events */
29 DWORD stg_pending_buf[N_PENDING_EVENTS]; /* their associated event numbers. */
34 * Function: initUserSignals()
36 * Initialize the console handling substrate.
41 console_handler = STG_SIG_DFL;
42 #if !defined (THREADED_RTS)
43 stg_pending_events = 0;
44 if (hConsoleEvent == INVALID_HANDLE_VALUE) {
46 CreateEvent ( NULL, /* default security attributes */
47 TRUE, /* manual-reset event */
48 FALSE, /* initially non-signalled */
56 freeSignalHandlers(void) {
60 /* Seems to be a bit of an orphan...where used? */
64 #if !defined (THREADED_RTS)
65 if (hConsoleEvent != INVALID_HANDLE_VALUE) {
66 CloseHandle(hConsoleEvent);
72 * Function: shutdown_handler()
74 * Local function that performs the default handling of Ctrl+C kind
75 * events; gently shutting down the RTS
77 * To repeat Signals.c remark -- user code may choose to override the
78 * default handler. Which is fine, assuming they put back the default
79 * handler when/if they de-install the custom handler.
82 static BOOL WINAPI shutdown_handler(DWORD dwCtrlType)
86 case CTRL_CLOSE_EVENT:
87 /* see generic_handler() comment re: this event */
90 case CTRL_BREAK_EVENT:
92 // If we're already trying to interrupt the RTS, terminate with
93 // extreme prejudice. So the first ^C tries to exit the program
94 // cleanly, and the second one just kills it.
95 if (sched_state >= SCHED_INTERRUPTING) {
96 stg_exit(EXIT_INTERRUPTED);
102 /* shutdown + logoff events are not handled here. */
110 * Function: initDefaultHandlers()
112 * Install any default signal/console handlers. Currently we install a
113 * Ctrl+C handler that shuts down the RTS in an orderly manner.
115 void initDefaultHandlers(void)
117 if ( !SetConsoleCtrlHandler(shutdown_handler, TRUE) ) {
118 errorBelch("warning: failed to install default console handler");
122 void resetDefaultHandlers(void)
124 if ( !SetConsoleCtrlHandler(shutdown_handler, FALSE) ) {
125 errorBelch("warning: failed to uninstall default console handler");
130 * Function: blockUserSignals()
132 * Temporarily block the delivery of further console events. Needed to
133 * avoid race conditions when GCing the stack of outstanding handlers or
134 * when emptying the stack by running the handlers.
138 blockUserSignals(void)
140 deliver_event = rtsFalse;
145 * Function: unblockUserSignals()
147 * The inverse of blockUserSignals(); re-enable the deliver of console events.
150 unblockUserSignals(void)
152 deliver_event = rtsTrue;
157 * Function: awaitUserSignals()
159 * Wait for the next console event. Currently a NOP (returns immediately.)
161 void awaitUserSignals(void)
167 #if !defined (THREADED_RTS)
169 * Function: startSignalHandlers()
171 * Run the handlers associated with the stacked up console events. Console
172 * event delivery is blocked for the duration of this call.
174 void startSignalHandlers(Capability *cap)
176 StgStablePtr handler;
178 if (console_handler < 0) {
183 ACQUIRE_LOCK(&sched_mutex);
185 handler = deRefStablePtr((StgStablePtr)console_handler);
186 while (stg_pending_events > 0) {
187 stg_pending_events--;
190 RtsFlags.GcFlags.initialStkSize,
192 (StgClosure *)handler,
194 stg_pending_buf[stg_pending_events]))));
197 RELEASE_LOCK(&sched_mutex);
198 unblockUserSignals();
200 #endif /* !THREADED_RTS */
203 * Function: markSignalHandlers()
205 * Evacuate the handler stack. _Assumes_ that console event delivery
206 * has already been blocked.
208 void markSignalHandlers (evac_fn evac STG_UNUSED, void *user STG_UNUSED)
210 // nothing to mark; the console handler is a StablePtr which is
211 // already treated as a root by the GC.
216 * Function: generic_handler()
218 * Local function which handles incoming console event (done in a sep OS thread),
219 * recording the event in stg_pending_events.
221 static BOOL WINAPI generic_handler(DWORD dwCtrlType)
223 /* Ultra-simple -- up the counter + signal a switch. */
225 case CTRL_CLOSE_EVENT:
226 /* Don't support the delivery of this event; if we
227 * indicate that we've handled it here and the Haskell handler
228 * doesn't take proper action (e.g., terminate the OS process),
229 * the user of the app will be unable to kill/close it. Not
230 * good, so disable the delivery for now.
234 if (!deliver_event) return TRUE;
236 #if defined(THREADED_RTS)
237 sendIOManagerEvent((StgWord8) ((dwCtrlType<<1) | 1));
239 if ( stg_pending_events < N_PENDING_EVENTS ) {
240 stg_pending_buf[stg_pending_events] = dwCtrlType;
241 stg_pending_events++;
244 // we need to wake up awaitEvent()
245 abandonRequestWait();
253 * Function: rts_InstallConsoleEvent()
255 * Install/remove a console event handler.
258 rts_InstallConsoleEvent(int action, StgStablePtr *handler)
260 StgInt previous_hdlr = console_handler;
264 console_handler = STG_SIG_IGN;
265 if ( !SetConsoleCtrlHandler(NULL, TRUE) ) {
266 errorBelch("warning: unable to ignore console events");
270 console_handler = STG_SIG_IGN;
271 if ( !SetConsoleCtrlHandler(NULL, FALSE) ) {
272 errorBelch("warning: unable to restore default console event handling");
277 // handler is stored in an MVar in the threaded RTS
278 console_handler = STG_SIG_HAN;
280 console_handler = (StgInt)*handler;
282 if (previous_hdlr < 0 || previous_hdlr == STG_SIG_HAN) {
283 /* Only install generic_handler() once */
284 if ( !SetConsoleCtrlHandler(generic_handler, TRUE) ) {
285 errorBelch("warning: unable to install console event handler");
291 if (previous_hdlr == STG_SIG_DFL ||
292 previous_hdlr == STG_SIG_IGN ||
293 previous_hdlr == STG_SIG_HAN) {
294 return previous_hdlr;
296 if (handler != NULL) {
297 *handler = (StgStablePtr)previous_hdlr;
304 * Function: rts_HandledConsoleEvent()
306 * Signal that a Haskell console event handler has completed its run.
307 * The explicit notification that a Haskell handler has completed is
308 * required to better handle the delivery of Ctrl-C/Break events whilst
309 * an async worker thread is handling a read request on stdin. The
310 * Win32 console implementation will abort such a read request when Ctrl-C
311 * is delivered. That leaves the worker thread in a bind: should it
312 * abandon the request (the Haskell thread reading from stdin has been
313 * thrown an exception to signal the delivery of Ctrl-C & hence have
314 * aborted the I/O request) or simply ignore the aborted read and retry?
315 * (the Haskell thread reading from stdin isn't concerned with the
316 * delivery and handling of Ctrl-C.) With both scenarios being
317 * possible, the worker thread needs to be told -- that is, did the
318 * console event handler cause the IO request to be abandoned?
322 rts_ConsoleHandlerDone (int ev USED_IF_NOT_THREADS)
324 #if !defined(THREADED_RTS)
325 if ( (DWORD)ev == CTRL_BREAK_EVENT ||
326 (DWORD)ev == CTRL_C_EVENT ) {
327 /* only these two cause stdin system calls to abort.. */
328 SetEvent(hConsoleEvent); /* event is manual-reset */
329 Sleep(0); /* yield */
330 ResetEvent(hConsoleEvent); /* turn it back off again */
331 // SDM: yeuch, this can't possibly work reliably.
332 // I'm not having it in THREADED_RTS.
337 #if !defined(THREADED_RTS)
339 * Function: rts_waitConsoleHandlerCompletion()
341 * Esoteric entry point used by worker thread that got woken
342 * up as part Ctrl-C delivery.
345 rts_waitConsoleHandlerCompletion()
347 /* As long as the worker doesn't need to do a multiple wait,
348 * let's keep this HANDLE private to this 'module'.
350 return (WaitForSingleObject(hConsoleEvent, INFINITE) == WAIT_OBJECT_0);