From 526a995d643560cc1ddf311e97138c64a77ce8e8 Mon Sep 17 00:00:00 2001 From: Alexander Iljin Date: Wed, 15 Jun 2016 16:19:52 +0300 Subject: [PATCH] VM: implement a ctrl-break handler thread (#1573) --- Nmakefile | 2 +- vm/os-unix.cpp | 8 ++++++++ vm/os-windows.cpp | 44 ++++++++++++++++++++++++++++++++++++++++++++ vm/safepoints.cpp | 9 ++++++++- vm/vm.cpp | 5 +++++ vm/vm.hpp | 6 ++++++ 6 files changed, 72 insertions(+), 2 deletions(-) diff --git a/Nmakefile b/Nmakefile index 689156a57f..ffd8b2d14e 100644 --- a/Nmakefile +++ b/Nmakefile @@ -8,7 +8,7 @@ GIT_LABEL = git-label-missing !IF DEFINED(PLATFORM) -LINK_FLAGS = /nologo shell32.lib +LINK_FLAGS = /nologo shell32.lib user32.lib CL_FLAGS = /nologo /O2 /WX /W3 /D_CRT_SECURE_NO_WARNINGS /DFACTOR_VERSION=$(VERSION) /DFACTOR_GIT_LABEL=$(GIT_LABEL) CL_FLAGS_VISTA = /D_WIN32_WINNT=0x0600 diff --git a/vm/os-unix.cpp b/vm/os-unix.cpp index 7c018eee0d..5d030d2c8e 100644 --- a/vm/os-unix.cpp +++ b/vm/os-unix.cpp @@ -474,6 +474,14 @@ void handle_ctrl_c() { sigaction_safe(SIGINT, &fep_sigaction, NULL); } +void factor_vm::primitive_disable_ctrl_break() { + stop_on_ctrl_break = false; +} + +void factor_vm::primitive_enable_ctrl_break() { + stop_on_ctrl_break = true; +} + void abort() { sig_t ret; do { diff --git a/vm/os-windows.cpp b/vm/os-windows.cpp index 145344d767..08651a1bd5 100644 --- a/vm/os-windows.cpp +++ b/vm/os-windows.cpp @@ -294,6 +294,50 @@ void handle_ctrl_c() { SetConsoleCtrlHandler(factor::ctrl_handler, TRUE); } +const int ctrl_break_sleep = 10; /* msec */ + +static DWORD WINAPI ctrl_break_thread_proc(LPVOID parent_vm) { + bool ctrl_break_handled = false; + factor_vm* vm = static_cast(parent_vm); + while (vm->stop_on_ctrl_break) { + if (GetAsyncKeyState(VK_CANCEL) >= 0) { /* Ctrl-Break is released. */ + ctrl_break_handled = false; /* Wait for the next press. */ + } else if (!ctrl_break_handled) { + /* Check if the VM thread has the same Id as the thread Id of the + currently active window. Note that thread Id is not a handle. */ + DWORD fg_thd_id = GetWindowThreadProcessId(GetForegroundWindow(), NULL); + if ((fg_thd_id == vm->thread_id) && !vm->fep_p) { + vm->enqueue_fep(); + ctrl_break_handled = true; + } + } + Sleep(ctrl_break_sleep); + } + return 0; +} + +void factor_vm::primitive_disable_ctrl_break() { + stop_on_ctrl_break = false; + if (ctrl_break_thread != NULL) { + DWORD wait_result = WaitForSingleObject(ctrl_break_thread, + 2 * ctrl_break_sleep); + if (wait_result != WAIT_OBJECT_0) + TerminateThread(ctrl_break_thread, 0); + CloseHandle(ctrl_break_thread); + ctrl_break_thread = NULL; + } +} + +void factor_vm::primitive_enable_ctrl_break() { + stop_on_ctrl_break = true; + if (ctrl_break_thread == NULL) { + DisableProcessWindowsGhosting(); + ctrl_break_thread = CreateThread(NULL, 0, factor::ctrl_break_thread_proc, + static_cast(this), 0, NULL); + SetThreadPriority(ctrl_break_thread, THREAD_PRIORITY_ABOVE_NORMAL); + } +} + void lock_console() {} void unlock_console() {} diff --git a/vm/safepoints.cpp b/vm/safepoints.cpp index ccfc27672e..180074a2d4 100644 --- a/vm/safepoints.cpp +++ b/vm/safepoints.cpp @@ -35,11 +35,18 @@ void factor_vm::handle_safepoint(cell pc) { faulting_p = false; if (atomic::load(&safepoint_fep_p)) { + atomic::store(&safepoint_fep_p, false); if (atomic::load(&sampling_profiler_p)) end_sampling_profiler(); std::cout << "Interrupted\n"; + if (stop_on_ctrl_break) { + /* Ctrl-Break throws an exception, interrupting the main thread, same + as the "t" command in the factorbug debugger. But for Ctrl-Break to + work we don't require the debugger to be activated, or even enabled. */ + general_error(ERROR_INTERRUPT, false_object, false_object); + FACTOR_ASSERT(false); + } factorbug(); - atomic::store(&safepoint_fep_p, false); } else if (atomic::load(&sampling_profiler_p)) { FACTOR_ASSERT(code->seg->in_segment_p(pc)); code_block* block = code->code_block_for_address(pc); diff --git a/vm/vm.cpp b/vm/vm.cpp index b0bae8b778..5e9f0209d6 100644 --- a/vm/vm.cpp +++ b/vm/vm.cpp @@ -7,6 +7,11 @@ factor_vm::factor_vm(THREADHANDLE thread) nursery(0, 0), faulting_p(false), thread(thread), +#if defined(WINDOWS) + thread_id(GetCurrentThreadId()), + ctrl_break_thread(NULL), + stop_on_ctrl_break(false), +#endif callback_id(0), c_to_factor_func(NULL), sampling_profiler_p(false), diff --git a/vm/vm.hpp b/vm/vm.hpp index 5fc4aabf54..900ca98dec 100644 --- a/vm/vm.hpp +++ b/vm/vm.hpp @@ -393,6 +393,7 @@ struct factor_vm { void factorbug_usage(bool advanced_p); void factorbug(); void primitive_die(); + volatile bool stop_on_ctrl_break; // arrays inline void set_array_nth(array* array, cell slot, cell value); @@ -701,6 +702,11 @@ struct factor_vm { // os-windows #if defined(WINDOWS) + /* Id of the main thread we run in. Used for Ctrl-Break handling. */ + DWORD thread_id; + + HANDLE ctrl_break_thread; + HANDLE sampler_thread; void sampler_thread_loop(); -- 2.34.1