15#if defined(GECODE_HAS_POSIX_BLACKBOX_EXEC) && !defined(_WIN32)
24#include <sys/socket.h>
35const size_t max_exec_response_size = 1024 * 1024;
39 int flags = fcntl(fd, F_GETFD);
43 return fcntl(fd, F_SETFD, flags | FD_CLOEXEC);
47dup_cloexec(
int fd,
int min_fd) {
50 nfd = fcntl(fd, F_DUPFD_CLOEXEC, min_fd);
54 if (errno != EINVAL) {
58 nfd = fcntl(fd, F_DUPFD, min_fd);
62 if (set_cloexec(nfd) != 0) {
72move_from_standard_fd(
int fd) {
73 if (fd > STDERR_FILENO) {
76 int nfd = dup_cloexec(fd, STDERR_FILENO + 1);
88 explicit FileDescriptor(
int fd0=-1) : fd(fd0) {}
89 ~FileDescriptor(
void) {
reset(); }
91 int get(
void)
const {
return fd; }
97 void reset(
int fd0=-1) {
106move_away_from_standard_fd(FileDescriptor &fd) {
107 int old = fd.release();
108 int nfd = move_from_standard_fd(old);
117class SpawnFileActions {
119 posix_spawn_file_actions_t actions;
122 SpawnFileActions(
void) : initialized(false) {}
123 ~SpawnFileActions(
void) {
125 posix_spawn_file_actions_destroy(&actions);
130 int err = posix_spawn_file_actions_init(&actions);
131 initialized = err == 0;
134 posix_spawn_file_actions_t *get(
void) {
return &actions; }
137class SpawnAttributes {
139 posix_spawnattr_t attr;
142 SpawnAttributes(
void) : initialized(false) {}
143 ~SpawnAttributes(
void) {
145 posix_spawnattr_destroy(&attr);
150 int err = posix_spawnattr_init(&attr);
151 initialized = err == 0;
154 posix_spawnattr_t *get(
void) {
return &attr; }
158create_socketpair(
int sv[2]) {
160 if (socketpair(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, sv) == 0) {
163 if (errno != EINVAL) {
167 if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv) != 0) {
170 if ((set_cloexec(sv[0]) != 0) || (set_cloexec(sv[1]) != 0)) {
181send_no_sigpipe(
int fd,
const char *data,
size_t size) {
183 return send(fd, data, size, MSG_NOSIGNAL);
186 return send(fd, data, size, 0);
192 sigaddset(&block, SIGPIPE);
193 bool blocked =
false;
194 bool was_pending =
false;
195 if (pthread_sigmask(SIG_BLOCK, &block, &old) == 0) {
197 if (sigpending(&pending) == 0) {
198 was_pending = sigismember(&pending, SIGPIPE) == 1;
201 ssize_t n = send(fd, data, size, 0);
202 if ((n == -1) && (errno == EPIPE) && !was_pending) {
203 const struct timespec timeout = {0, 0};
204 sigtimedwait(&block, NULL, &timeout);
207 pthread_sigmask(SIG_SETMASK, &old, NULL);
219 static std::string last_error(
const std::string &prefix) {
220 return prefix +
" (errno " + std::to_string(errno) +
")";
223 static void sleep_grace_period(
void) {
224 struct timespec remaining = {0, 10000000};
225 while ((nanosleep(&remaining, &remaining) == -1) && (errno == EINTR)) {}
228 static bool child_exited(pid_t pid) {
232 if (waitid(P_PID, pid, &info, WEXITED | WNOHANG | WNOWAIT) == 0) {
233 return info.si_pid != 0;
235 }
while (errno == EINTR);
239 static void signal_group(pid_t pid,
int signal) {
240 if ((kill(-pid, signal) == -1) && (errno == ESRCH)) {
245 static void wait_group(pid_t pid,
int attempts) {
246 for (
int i = 0;
i < attempts;
i++) {
247 if ((kill(-pid, 0) == -1) && (errno == ESRCH)) {
250 if (child_exited(pid)) {
253 sleep_grace_period();
257 static void terminate_child(pid_t pid) {
263 signal_group(pid, SIGTERM);
264 wait_group(pid, 100);
265 signal_group(pid, SIGKILL);
267 if (waitpid(pid, &status, 0) != -1) {
270 }
while (errno == EINTR);
273 static void check_sigchld(
void) {
274 struct sigaction action;
275 if (sigaction(SIGCHLD, NULL, &action) != 0) {
276 throw Error(
"BlackBoxExec", last_error(
"SIGCHLD query failed"));
278 if ((action.sa_handler != SIG_DFL)
280 || (action.sa_flags & SA_NOCLDWAIT)
283 throw Error(
"BlackBoxExec",
284 "Cannot start a blackbox process unless SIGCHLD uses "
285 "SIG_DFL without SA_NOCLDWAIT");
289 void open_posix(
const std::string &program,
290 const std::vector<std::string> &args);
291 void close_posix(
void);
294 PosixProcessSession(
const std::string &program,
const std::vector<std::string> &args)
295 : child(-1), pipe_send(-1), file_receive(NULL)
297 open_posix(program, args);
300 ~PosixProcessSession(
void) { close(); }
302 std::string
exchange(
const std::string &out_buf) {
303 const char *p = out_buf.c_str();
304 size_t remaining = out_buf.size();
305 while (remaining > 0) {
306 ssize_t n = send_no_sigpipe(pipe_send, p, remaining);
308 if (errno == EINTR) {
311 throw Error(
"BlackBoxExec",
312 "Writing blackbox process input failed with errno " +
313 std::to_string(errno));
316 throw Error(
"BlackBoxExec",
317 "Writing blackbox process input wrote zero bytes");
320 remaining -=
static_cast<size_t>(n);
323 std::string in_buffer;
326 int ch = fgetc(file_receive);
328 if (feof(file_receive)) {
329 throw Error(
"BlackBoxExec",
330 "Blackbox process provided an incomplete response");
334 clearerr(file_receive);
337 throw Error(
"BlackBoxExec",
338 std::string(
"Reading blackbox process output from pipe "
339 "failed with errno ") +
340 std::to_string(err));
342 in_buffer +=
static_cast<char>(ch);
343 if (in_buffer.size() > max_exec_response_size) {
344 throw Error(
"BlackBoxExec",
345 "Blackbox process response exceeds the size limit");
360PosixProcessSession::open_posix(
const std::string& program,
361 const std::vector<std::string>& args) {
365 std::vector<char *> argv;
366 argv.reserve(args.size() + 2);
367 argv.push_back(
const_cast<char *
>(program.c_str()));
368 for (
const std::string &a : args) {
369 argv.push_back(
const_cast<char *
>(
a.c_str()));
371 argv.push_back(
nullptr);
375 FileDescriptor child_in[2];
376 FileDescriptor child_out[2];
378 if (create_socketpair(fds) != 0) {
379 throw Error(
"BlackBoxExec", last_error(
"stdin socket creation failed"));
381 child_in[READ].reset(fds[READ]);
382 child_in[WRITE].reset(fds[WRITE]);
383 if (create_socketpair(fds) != 0) {
384 throw Error(
"BlackBoxExec", last_error(
"stdout socket creation failed"));
386 child_out[READ].reset(fds[READ]);
387 child_out[WRITE].reset(fds[WRITE]);
388 FileDescriptor *session_fds[] = {
389 &child_in[READ], &child_in[WRITE],
390 &child_out[READ], &child_out[WRITE]
392 for (FileDescriptor *fd : session_fds) {
393 if (move_away_from_standard_fd(*fd) == -1) {
394 throw Error(
"BlackBoxExec",
395 last_error(
"moving session descriptors away from stdio "
400 SpawnFileActions actions;
401 int err = actions.init();
404 throw Error(
"BlackBoxExec", last_error(
"spawn file action init failed"));
407 SpawnAttributes attr;
411 throw Error(
"BlackBoxExec", last_error(
"spawn attribute init failed"));
414 err = posix_spawnattr_setpgroup(attr.get(), 0);
416 short flags = POSIX_SPAWN_SETPGROUP;
417#if defined(GECODE_HAS_POSIX_SPAWN_CLOEXEC_DEFAULT) && \
418 defined(GECODE_HAS_POSIX_SPAWN_ADDINHERIT_NP)
419 flags |= POSIX_SPAWN_CLOEXEC_DEFAULT;
421 err = posix_spawnattr_setflags(attr.get(), flags);
424 err = posix_spawn_file_actions_adddup2(actions.get(),
425 child_in[READ].get(),
429 err = posix_spawn_file_actions_adddup2(actions.get(),
430 child_out[WRITE].get(),
433#if defined(GECODE_HAS_POSIX_SPAWN_CLOEXEC_DEFAULT) && \
434 defined(GECODE_HAS_POSIX_SPAWN_ADDINHERIT_NP)
436 err = posix_spawn_file_actions_addinherit_np(actions.get(),
439#elif defined(GECODE_HAS_POSIX_SPAWN_ADDCLOSEFROM_NP)
441 err = posix_spawn_file_actions_addclosefrom_np(actions.get(),
446 err = posix_spawnp(&child, program.c_str(), actions.get(), attr.get(),
447 argv.data(), environ);
452 throw Error(
"BlackBoxExec", last_error(
"starting blackbox process failed"));
455 child_in[READ].reset();
456 child_out[WRITE].reset();
460 if (setsockopt(child_in[WRITE].get(), SOL_SOCKET, SO_NOSIGPIPE, &nosigpipe,
461 sizeof(nosigpipe)) != 0) {
463 terminate_child(child);
466 throw Error(
"BlackBoxExec", last_error(
"SO_NOSIGPIPE setup failed"));
469 FILE *receive = fdopen(child_out[READ].get(),
"r");
470 if (receive == NULL) {
472 terminate_child(child);
475 throw Error(
"BlackBoxExec", last_error(
"fdopen failed"));
477 file_receive = receive;
478 child_out[READ].release();
479 pipe_send = child_in[WRITE].release();
483PosixProcessSession::close_posix(
void) {
484 if (pipe_send != -1) {
488 if (file_receive != NULL) {
489 fclose(file_receive);
493 terminate_child(child);
502 const std::vector<std::string>& args) {
503 return new PosixProcessSession(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.
void reset(void)
Reset all failpoint state.
void exchange(Type &a, Type &b, Less &less)
Exchange elements according to order.
Gecode toplevel namespace
Gecode::FloatVal a(-8, 5)
Gecode::IntArgs i({1, 2, 3, 4})