13#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0600)
15#define _WIN32_WINNT 0x0600
17#if !defined(WINVER) || (WINVER < 0x0600)
31const size_t max_exec_response_size = 1024 * 1024;
34utf8_to_wide(
const std::string &s) {
36 return std::wstring();
38 int n = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(),
39 static_cast<int>(s.size()), NULL, 0);
41 throw Error(
"Blackbox",
"Invalid UTF-8 string in blackbox path or argument");
43 std::wstring w(
static_cast<size_t>(n), L
'\0');
44 if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, s.c_str(),
45 static_cast<int>(s.size()), &w[0], n) == 0) {
46 throw Error(
"Blackbox",
"Invalid UTF-8 string in blackbox path or argument");
52windows_error(
const std::string &prefix, DWORD err) {
53 return prefix +
" (Windows error " + std::to_string(err) +
")";
60 explicit WindowsHandle(HANDLE handle0=NULL) : handle(handle0) {}
61 ~WindowsHandle(
void) {
reset(); }
63 WindowsHandle(
const WindowsHandle &) =
delete;
64 WindowsHandle &operator=(
const WindowsHandle &) =
delete;
66 HANDLE get(
void)
const {
return handle; }
71 HANDLE release(
void) {
72 HANDLE handle0 = handle;
76 bool valid(
void)
const {
77 return (handle != NULL) && (handle != INVALID_HANDLE_VALUE);
79 void reset(HANDLE handle0=NULL) {
87class WindowsAttributeList {
89 std::vector<char> buffer;
90 LPPROC_THREAD_ATTRIBUTE_LIST list;
93 WindowsAttributeList(
void) : list(NULL), initialized(false) {}
94 ~WindowsAttributeList(
void) {
96 DeleteProcThreadAttributeList(list);
102 InitializeProcThreadAttributeList(NULL, 1, 0, &size);
104 throw Error(
"BlackBoxExec",
105 windows_error(
"ProcThreadAttributeList size query failed",
109 list =
reinterpret_cast<LPPROC_THREAD_ATTRIBUTE_LIST
>(buffer.data());
110 if (!InitializeProcThreadAttributeList(list, 1, 0, &size)) {
111 throw Error(
"BlackBoxExec",
112 windows_error(
"InitializeProcThreadAttributeList failed",
118 void set_inherited_handles(HANDLE *handles, DWORD count) {
119 if (!UpdateProcThreadAttribute(list, 0, PROC_THREAD_ATTRIBUTE_HANDLE_LIST,
120 handles,
sizeof(HANDLE) * count, NULL,
122 throw Error(
"BlackBoxExec",
123 windows_error(
"PROC_THREAD_ATTRIBUTE_HANDLE_LIST failed",
128 LPPROC_THREAD_ATTRIBUTE_LIST get(
void)
const {
return list; }
131qualified_path(
const std::wstring &program) {
132 return (program.find_first_of(L
"\\/") != std::wstring::npos) ||
133 ((program.size() > 1) && (program[1] == L
':'));
143 static std::string last_error(
const std::string &prefix) {
144 return prefix +
" (Windows error " + std::to_string(GetLastError()) +
")";
147 static void close_handle(HANDLE &h) {
154 static std::wstring quote_argument(
const std::wstring &arg) {
155 std::wstring q(L
"\"");
156 unsigned int backslashes = 0;
157 for (
wchar_t ch : arg) {
160 }
else if (ch == L
'"') {
161 q.append(backslashes * 2 + 1, L
'\\');
165 q.append(backslashes, L
'\\');
170 q.append(backslashes * 2, L
'\\');
175 void open_windows(
const std::string &program,
176 const std::vector<std::string> &args);
177 void close_windows(
void);
180 WindowsProcessSession(
const std::string &program,
const std::vector<std::string> &args)
181 : job(NULL), process(NULL), pipe_send(NULL), pipe_receive(NULL)
183 open_windows(program, args);
186 ~WindowsProcessSession(
void) { close(); }
188 std::string
exchange(
const std::string &out_buf) {
190 while (written < out_buf.size()) {
193 static_cast<DWORD
>(out_buf.size() - written);
195 WriteFile(pipe_send, out_buf.data() + written, remaining, &count,
197 if (!success || count == 0) {
198 throw Error(
"BlackBoxExec",
199 last_error(
"Writing blackbox process input failed"));
205 std::ostringstream oss;
206 size_t response_size = 0;
207 while (c[0] !=
'\n') {
209 BOOL success = ReadFile(pipe_receive, c,
sizeof(c) - 1, &count, NULL);
211 if (GetLastError() == ERROR_BROKEN_PIPE) {
212 throw Error(
"BlackBoxExec",
213 "Blackbox process provided an incomplete response");
217 "Failed to read blackbox process output from pipe");
218 }
else if (count == 0) {
219 throw Error(
"BlackBoxExec",
220 "Blackbox process provided an incomplete response");
223 if (++response_size > max_exec_response_size) {
224 throw Error(
"BlackBoxExec",
225 "Blackbox process response exceeds the size limit");
238WindowsProcessSession::open_windows(
const std::string &program,
239 const std::vector<std::string> &args) {
242 std::wstring program_w = utf8_to_wide(program);
243 std::wstring prog = quote_argument(program_w);
244 for (
const std::string &a : args) {
246 prog += quote_argument(utf8_to_wide(a));
248 std::vector<wchar_t> cmdline(prog.begin(), prog.end());
249 cmdline.push_back(L
'\0');
251 SECURITY_ATTRIBUTES saAttr;
252 saAttr.nLength =
sizeof(SECURITY_ATTRIBUTES);
253 saAttr.bInheritHandle = TRUE;
254 saAttr.lpSecurityDescriptor = NULL;
256 WindowsHandle child_stdin_read;
257 WindowsHandle child_stdin_write;
258 WindowsHandle child_stdout_read;
259 WindowsHandle child_stdout_write;
260 WindowsHandle child_stderr_write;
261 if (!CreatePipe(child_stdout_read.put(), child_stdout_write.put(), &saAttr,
263 throw Error(
"BlackBoxExec", last_error(
"Stdout CreatePipe failed"));
265 if (!SetHandleInformation(child_stdout_read.get(), HANDLE_FLAG_INHERIT, 0)) {
266 throw Error(
"BlackBoxExec",
267 last_error(
"Stdout SetHandleInformation failed"));
269 if (!CreatePipe(child_stdin_read.put(), child_stdin_write.put(), &saAttr,
271 throw Error(
"BlackBoxExec", last_error(
"Stdin CreatePipe failed"));
273 if (!SetHandleInformation(child_stdin_write.get(), HANDLE_FLAG_INHERIT, 0)) {
274 throw Error(
"BlackBoxExec",
275 last_error(
"Stdin SetHandleInformation failed"));
278 HANDLE parent_stderr = GetStdHandle(STD_ERROR_HANDLE);
279 if ((parent_stderr != NULL) && (parent_stderr != INVALID_HANDLE_VALUE)) {
280 if (!DuplicateHandle(GetCurrentProcess(), parent_stderr,
281 GetCurrentProcess(), child_stderr_write.put(), 0, TRUE,
282 DUPLICATE_SAME_ACCESS)) {
283 throw Error(
"BlackBoxExec",
284 last_error(
"stderr DuplicateHandle failed"));
287 HANDLE nul = CreateFileW(L
"NUL", GENERIC_WRITE,
288 FILE_SHARE_READ | FILE_SHARE_WRITE, &saAttr,
289 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
290 if (nul == INVALID_HANDLE_VALUE) {
291 throw Error(
"BlackBoxExec", last_error(
"stderr NUL CreateFile failed"));
293 child_stderr_write.reset(nul);
296 WindowsAttributeList attr_list;
298 PROCESS_INFORMATION piProcInfo;
299 STARTUPINFOEXW siStartInfo;
300 ZeroMemory(&piProcInfo,
sizeof(PROCESS_INFORMATION));
301 ZeroMemory(&siStartInfo,
sizeof(STARTUPINFOEXW));
302 siStartInfo.StartupInfo.cb =
sizeof(STARTUPINFOEXW);
303 siStartInfo.StartupInfo.hStdOutput = child_stdout_write.get();
304 siStartInfo.StartupInfo.hStdInput = child_stdin_read.get();
305 siStartInfo.StartupInfo.hStdError = child_stderr_write.get();
306 siStartInfo.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
308 HANDLE inherit_handles[3] = {child_stdin_read.get(), child_stdout_write.get(),
309 child_stderr_write.get()};
310 attr_list.set_inherited_handles(inherit_handles, 3);
311 siStartInfo.lpAttributeList = attr_list.get();
313 WindowsHandle process_job(CreateJobObjectW(NULL, NULL));
314 if (!process_job.valid()) {
315 throw Error(
"BlackBoxExec", last_error(
"CreateJobObject failed"));
317 JOBOBJECT_EXTENDED_LIMIT_INFORMATION job_info;
318 ZeroMemory(&job_info,
sizeof(job_info));
319 job_info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
320 if (!SetInformationJobObject(process_job.get(),
321 JobObjectExtendedLimitInformation, &job_info,
323 throw Error(
"BlackBoxExec", last_error(
"SetInformationJobObject failed"));
326 BOOL processStarted =
327 CreateProcessW(qualified_path(program_w) ? program_w.c_str() : NULL,
332 EXTENDED_STARTUPINFO_PRESENT | CREATE_SUSPENDED,
335 &siStartInfo.StartupInfo,
338 if (!processStarted) {
339 throw Error(
"BlackBoxExec",
340 windows_error(
"starting blackbox process failed for program `" +
341 program +
"'", GetLastError()));
343 WindowsHandle process_handle(piProcInfo.hProcess);
344 WindowsHandle thread_handle(piProcInfo.hThread);
345 if (!AssignProcessToJobObject(process_job.get(), process_handle.get())) {
346 DWORD err = GetLastError();
347 DWORD terminate_err = ERROR_SUCCESS;
348 if (!TerminateProcess(process_handle.get(), 1)) {
349 terminate_err = GetLastError();
351 DWORD
wait = WaitForSingleObject(process_handle.get(), 5000);
352 std::string message = windows_error(
353 "Unable to assign blackbox process to required job", err);
354 if (terminate_err != ERROR_SUCCESS) {
355 message +=
"; " + windows_error(
"TerminateProcess cleanup failed",
358 if (
wait == WAIT_FAILED) {
359 message +=
"; " + last_error(
"process cleanup wait failed");
360 }
else if (
wait == WAIT_TIMEOUT) {
361 message +=
"; process cleanup timed out";
363 throw Error(
"BlackBoxExec", message);
366 if (ResumeThread(thread_handle.get()) ==
static_cast<DWORD
>(-1)) {
367 DWORD err = GetLastError();
368 DWORD terminate_err = ERROR_SUCCESS;
369 if (!TerminateJobObject(process_job.get(), 1)) {
370 terminate_err = GetLastError();
372 HANDLE assigned_job = process_job.release();
373 DWORD close_err = ERROR_SUCCESS;
374 if (!CloseHandle(assigned_job)) {
375 close_err = GetLastError();
377 DWORD
wait = WaitForSingleObject(process_handle.get(), 5000);
378 std::string message = windows_error(
379 "ResumeThread failed for blackbox process", err);
380 if (terminate_err != ERROR_SUCCESS) {
381 message +=
"; " + windows_error(
"TerminateJobObject cleanup failed",
384 if (close_err != ERROR_SUCCESS) {
385 message +=
"; " + windows_error(
"job cleanup close failed", close_err);
387 if (
wait == WAIT_FAILED) {
388 message +=
"; " + last_error(
"process cleanup wait failed");
389 }
else if (
wait == WAIT_TIMEOUT) {
390 message +=
"; process cleanup timed out";
392 throw Error(
"BlackBoxExec", message);
395 pipe_send = child_stdin_write.release();
396 pipe_receive = child_stdout_read.release();
397 process = process_handle.release();
398 job = process_job.release();
402WindowsProcessSession::close_windows(
void) {
403 close_handle(pipe_send);
404 close_handle(pipe_receive);
405 if (process != NULL) {
406 DWORD
wait = WaitForSingleObject(process, 1000);
407 if (
wait == WAIT_TIMEOUT) {
409 TerminateJobObject(job, 1);
411 TerminateProcess(process, 1);
413 WaitForSingleObject(process, 5000);
415 close_handle(process);
423 const std::vector<std::string>& args) {
424 return new WindowsProcessSession(program, args);
Platform process session used by the executable blackbox backend.
Exception class for FlatZinc errors
Interpreter for the FlatZinc language.
BlackBoxProcessSession * create_blackbox_process(const std::string &, const std::vector< std::string > &)
Create the process implementation selected for the target platform.
bool valid(const FloatVal &n)
Return whether float n is a valid number.
unsigned int size(I &i)
Size of all ranges of range iterator i.
void reset(void)
Reset all failpoint state.
void exchange(Type &a, Type &b, Less &less)
Exchange elements according to order.
Gecode toplevel namespace
void count(Home home, const IntVarArgs &x, int n, IntRelType irt, int m, IntPropLevel ipl=IPL_DEF)
Post propagator for .
void wait(Home home, FloatVar x, std::function< void(Space &home)> c)
Execute c when x becomes assigned.