1 /* -*- mode: C++; c-basic-offset: 2; indent-tabs-mode: nil -*- */ 2 3 /* 4 * Main authors: 5 * Jason Nguyen <jason.nguyen@monash.edu> 6 */ 7 8 /* This Source Code Form is subject to the terms of the Mozilla Public 9 * License, v. 2.0. If a copy of the MPL was not distributed with this 10 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 11 12 #pragma once 13 14 #ifdef _WIN32 15 16 #define NOMINMAX // Ensure the words min/max remain available 17 #include <Windows.h> 18 #undef ERROR 19 #include <sstream> 20 #include <thread> 21 22 namespace MiniZinc { 23 // Listens for a message on the named pipe \\.\pipe\minizinc-PID 24 // Triggers a Ctrl+C when an empty message is received. 25 class InterruptListener { 26 public: run()27 static InterruptListener& run() { 28 static InterruptListener instance; 29 return instance; 30 } 31 InterruptListener(InterruptListener const&) = delete; 32 void operator=(InterruptListener const&) = delete; 33 ~InterruptListener()34 ~InterruptListener() { 35 SetConsoleCtrlHandler(CtrlHandler, FALSE); 36 SetEvent(hEvents[0]); 37 thread.join(); 38 CloseHandle(hNamedPipe); 39 CloseHandle(hEvents[0]); 40 CloseHandle(hEvents[1]); 41 } 42 43 private: 44 std::thread thread; 45 HANDLE hNamedPipe; 46 InterruptListener()47 InterruptListener() { 48 // Setup events 49 hEvents[0] = CreateEvent(NULL, FALSE, FALSE, NULL); // Signalled when thread needs to exit 50 hEvents[1] = CreateEvent(NULL, FALSE, FALSE, NULL); // Signalled on pipe events 51 52 // Setup a named pipe so that the IDE can trigger an interrupt 53 std::stringstream ss; 54 ss << "\\\\.\\pipe\\minizinc-" << GetCurrentProcessId(); 55 std::string pipeName = ss.str(); 56 hNamedPipe = CreateNamedPipe(pipeName.c_str(), PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, 57 PIPE_TYPE_MESSAGE, 1, 0, 0, 0, NULL); 58 59 if (hEvents[0] && hEvents[1] && hNamedPipe) { 60 SetConsoleCtrlHandler(CtrlHandler, TRUE); 61 thread = std::thread(&InterruptListener::listen, this); 62 } 63 } 64 listen()65 void listen() { 66 OVERLAPPED ol; 67 68 // Connect pipe 69 ZeroMemory(&ol, sizeof(OVERLAPPED)); 70 ol.hEvent = hEvents[1]; 71 ConnectNamedPipe(hNamedPipe, &ol); 72 DWORD ev = WaitForMultipleObjects(2, &hEvents[0], FALSE, INFINITE); 73 if (ev - WAIT_OBJECT_0 == 0) { 74 return; 75 } 76 77 // Listen for pings on pipe 78 while (true) { 79 ZeroMemory(&ol, sizeof(OVERLAPPED)); 80 ol.hEvent = hEvents[1]; 81 ReadFile(hNamedPipe, NULL, 0, NULL, &ol); 82 83 DWORD ev = WaitForMultipleObjects(2, &hEvents[0], FALSE, INFINITE); 84 if (ev - WAIT_OBJECT_0 == 0) { 85 return; 86 } 87 88 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0); 89 } 90 } 91 92 static HANDLE hEvents[2]; 93 CtrlHandler(DWORD fdwCtrlType)94 static BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) { 95 // Tell thread to stop 96 SetEvent(hEvents[0]); 97 return FALSE; 98 } 99 }; 100 101 HANDLE InterruptListener::hEvents[2]; 102 103 } // namespace MiniZinc 104 105 #endif 106