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