windows: Better handling of Ctrl-C

This builds upon the changes made in 2195046365. Using signal() does not
produce reliable results so SetConsoleCtrlHandler is used, and the handler
is installed only once during initialization instead of removing it in
mp_hal_set_interrupt_char when it is not strictly needed anymore, since
removing it might lead to Ctrl-C events being missed because they are
fired on a seperate thread which might only become alive after the handler
was removed.
This commit is contained in:
stijn 2015-12-21 10:17:37 +01:00 committed by Paul Sokolovsky
parent 1b7f622410
commit a5aa03acaf
2 changed files with 19 additions and 5 deletions

View File

@ -26,9 +26,13 @@
#include <stdlib.h>
#include <stdio.h>
#include <windows.h>
#include "sleep.h"
extern BOOL WINAPI console_sighandler(DWORD evt);
void init() {
SetConsoleCtrlHandler(console_sighandler, TRUE);
init_sleep();
#ifdef __MINGW32__
putenv("PRINTF_EXPONENT_DIGITS=2");
@ -40,5 +44,6 @@ void init() {
}
void deinit() {
SetConsoleCtrlHandler(console_sighandler, FALSE);
deinit_sleep();
}

View File

@ -30,7 +30,6 @@
#include <windows.h>
#include <unistd.h>
#include <signal.h>
HANDLE std_in = NULL;
HANDLE con_out = NULL;
@ -67,15 +66,27 @@ void mp_hal_stdio_mode_orig(void) {
SetConsoleMode(std_in, orig_mode);
}
STATIC void sighandler(int signum) {
if (signum == SIGINT) {
// Handler to be installed by SetConsoleCtrlHandler, currently used only to handle Ctrl-C.
// This handler has to be installed just once (this has to be done elswhere in init code).
// Previous versions of the mp_hal code would install a handler whenever Ctrl-C input is
// allowed and remove the handler again when it is not. That is not necessary though (1),
// and it might introduce problems (2) because console notifications are delivered to the
// application in a seperate thread.
// (1) mp_hal_set_interrupt_char effectively enables/disables processing of Ctrl-C via the
// ENABLE_PROCESSED_INPUT flag so in raw mode console_sighandler won't be called.
// (2) if mp_hal_set_interrupt_char would remove the handler while Ctrl-C was issued earlier,
// the thread created for handling it might not be running yet so we'd miss the notification.
BOOL WINAPI console_sighandler(DWORD evt) {
if (evt == CTRL_C_EVENT) {
if (MP_STATE_VM(mp_pending_exception) == MP_STATE_VM(keyboard_interrupt_obj)) {
// this is the second time we are called, so die straight away
exit(1);
}
mp_obj_exception_clear_traceback(MP_STATE_VM(keyboard_interrupt_obj));
MP_STATE_VM(mp_pending_exception) = MP_STATE_VM(keyboard_interrupt_obj);
return TRUE;
}
return FALSE;
}
void mp_hal_set_interrupt_char(char c) {
@ -85,13 +96,11 @@ void mp_hal_set_interrupt_char(char c) {
GetConsoleMode(std_in, &mode);
mode |= ENABLE_PROCESSED_INPUT;
SetConsoleMode(std_in, mode);
signal(SIGINT, sighandler);
} else {
DWORD mode;
GetConsoleMode(std_in, &mode);
mode &= ~ENABLE_PROCESSED_INPUT;
SetConsoleMode(std_in, mode);
signal(SIGINT, SIG_DFL);
}
}