summaryrefslogtreecommitdiff
path: root/src/autoplay/child_process.cc
diff options
context:
space:
mode:
authorScott Gasch <[email protected]>2016-06-01 18:58:58 -0700
committerScott Gasch <[email protected]>2016-06-01 18:58:58 -0700
commit3fd43cd5fcb22bb65bf2a92a25d95d801b11c9e0 (patch)
tree9b6443235d16ba17f094a1a1c7ae53d2bcb9267b /src/autoplay/child_process.cc
Initial checkin for typhoon chess engine.
Diffstat (limited to 'src/autoplay/child_process.cc')
-rw-r--r--src/autoplay/child_process.cc188
1 files changed, 188 insertions, 0 deletions
diff --git a/src/autoplay/child_process.cc b/src/autoplay/child_process.cc
new file mode 100644
index 0000000..6486a0e
--- /dev/null
+++ b/src/autoplay/child_process.cc
@@ -0,0 +1,188 @@
+#include "child_process.h"
+
+#include <vector>
+#include <cstdio>
+#include <cstdlib>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+namespace util {
+
+void ChildProcess::ParseCommandline(vector<char *> *args) {
+ char *p = commandline_;
+ bool in_quote;
+ do {
+ // Find the start of a chunk.
+ while(*p && isspace(*p)) p++;
+ if (!*p) break;
+ args->push_back(p);
+ in_quote = false;
+
+ // Find the end of the chunk.
+ while (*p) {
+ if (*p == '"') {
+ in_quote = !in_quote;
+ } else if (isspace(*p) && !in_quote) {
+ break;
+ }
+ p++;
+ }
+ if (!*p) break;
+ *p = '\0';
+ p++;
+ } while(1);
+ args->push_back(NULL);
+}
+
+static volatile bool _child_ready = false;
+static void SignalHandler(int signo) {
+ printf("SIGNAL!\n");
+ _child_ready = true;
+}
+
+void ChildProcess::Start() {
+ Stop();
+ vector<char *> args;
+ ParseCommandline(&args);
+
+ if (args.size() > 1) {
+ if (pipe(read_pipe_) != 0) {
+ perror("pipe");
+ exit(-1);
+ }
+ if (pipe(write_pipe_) != 0) {
+ perror("pipe");
+ exit(-1);
+ }
+
+ // Prepare a signal handler to tell us when the child process is
+ // ready.
+ _child_ready = false;
+ if (signal(SIGUSR1, SignalHandler) == SIG_ERR) {
+ perror("signal");
+ exit(-1);
+ }
+ sigset_t zeromask, mask, orig;
+ sigemptyset(&zeromask);
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGUSR1);
+ if (sigprocmask(SIG_BLOCK, &mask, &orig) < 0) {
+ perror("sigprocmask");
+ exit(-1);
+ }
+
+ // Fork.
+ pid_ = fork();
+ if (pid_ < 0) {
+ perror("fork");
+ exit(-1);
+ }
+ if (pid_ == 0) {
+ printf("In the child process...\n");
+
+ // In the child process we do not need the pipes for parent
+ // reads and writes.
+ close(write_pipe_[1]);
+ close(read_pipe_[0]);
+ if (dup2(write_pipe_[0], fileno(stdin)) < 0) {
+ perror("dup2");
+ exit(-1);
+ }
+ close(write_pipe_[0]);
+ if (dup2(read_pipe_[1], fileno(stdout)) < 0) {
+ perror("dup2");
+ exit(-1);
+ }
+ if (dup2(fileno(stdout), fileno(stderr)) < 0) {
+ perror("dup2");
+ exit(-1);
+ }
+ close(read_pipe_[1]);
+
+ // Tell the parent that the pipes are set up then exec the child
+ // image.
+ kill(getppid(), SIGUSR1);
+ execvp(args[0], &(args[0]));
+ perror("exec");
+ exit(-1);
+ } else {
+ printf("In the parent process waiting...\n");
+ while(!_child_ready) {
+ sigsuspend(&zeromask);
+ }
+ printf("PARENT WAKEUP!\n");
+ _child_ready = false;
+ if (sigprocmask(SIG_SETMASK, &orig, NULL) < 0) {
+ perror("sigprocmask");
+ exit(-1);
+ }
+ close(write_pipe_[0]);
+ close(read_pipe_[1]);
+ from_ = fdopen(read_pipe_[0], "r");
+ to_ = fdopen(write_pipe_[1], "w");
+ setbuf(from_, NULL);
+ setbuf(to_, NULL);
+ running_ = true;
+ name_ = args[0];
+ }
+ }
+}
+
+void ChildProcess::Stop() {
+ if (!running_) {
+ return;
+ }
+ fclose(to_);
+ fclose(from_);
+ close(write_pipe_[1]);
+ close(read_pipe_[0]);
+ kill(pid_, SIGTERM);
+ int x;
+ waitpid(pid_, &x, 0);
+}
+
+void ChildProcess::Send(char *line) {
+ if (!running_) {
+ return;
+ }
+ fprintf(to_, "%s", line);
+ printf("%s sent: %s", name_, line);
+}
+
+void ChildProcess::Receive(char *line, int size) {
+ if (!size) return;
+ *line = '\0';
+ if (!running_) {
+ return;
+ }
+ if (!Poll()) {
+ return;
+ }
+ fgets(line, size, from_);
+ printf("%s recv: %s\n", name_, line);
+}
+
+bool ChildProcess::Poll() {
+ fd_set myset;
+ FD_ZERO(&myset);
+ FD_SET(read_pipe_[0], &myset);
+ timeval mytime;
+ mytime.tv_sec = 0;
+ mytime.tv_usec = 0;
+ return select(read_pipe_[0] + 1, &myset, NULL, NULL, &mytime) > 0;
+}
+
+void ChildProcess::Flush() {
+ static char line[1000];
+ while (Poll()) {
+ Receive(line, 1000);
+ printf("flush: %s", line);
+ if (!line[0]) {
+ break;
+ }
+ }
+}
+
+} // namespace