7 #include "thread_util.h"
16 * @param executablePath - path to the program that will be run
18 * Creates two pipes - one for each direction between the parent process and the AI program
20 * The child process closes unused sides of the pipe, and then calls exec to replace itself with the AI program
21 * The parent process closes unused sides of the pipe, and sets up member variables - associates streams with the pipe fd's for convenience.
23 Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0)
25 //See if file exists and is executable...
26 if (access(executablePath, X_OK) != 0)
32 int readPipe[2]; int writePipe[2];
33 assert(pipe(readPipe) == 0);
34 assert(pipe(writePipe) == 0);
39 close(readPipe[0]); //close end that parent reads from
40 close(writePipe[1]); //close end that parent writes to
42 //TODO: Fix possible bug here if the process is already a daemon
43 assert(writePipe[0] != 0 && readPipe[1] != 1);
44 dup2(writePipe[0],0); close(writePipe[0]); //pipe end child reads from goes to STDIN
45 dup2(readPipe[1], 1); close(readPipe[1]); //pipe end child writes to goes to STDOUT
47 //TODO: Somehow force the exec'd process to be unbuffered
48 setbuf(stdin, NULL); //WARNING: These lines don't appear to have any affect
49 setbuf(stdout, NULL); //You should add them at the start of the wrapped program.
50 //If your wrapped program is not written in C/C++, you will probably have a problem
53 if (access(executablePath, X_OK) == 0) //Check we STILL have permissions to start the file
54 execl(executablePath, executablePath, (char*)(NULL)); ///Replace process with desired executable
56 fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath);
57 exit(EXIT_FAILURE); //We will probably have to terminate the whole program if this happens
61 close(writePipe[0]); //close end that child writes to
62 close(readPipe[1]); //close end that child reads from
64 input = fdopen(readPipe[0],"r"); output = fdopen(writePipe[1],"w");
73 * Writes EOF to the wrapped program and then closes all streams
74 * Kills the wrapped program if it does not exit within 1 second.
78 if (Running()) //Check if the process created is still running...
80 //fputc(EOF, output); //If it was, tell it to stop with EOF
82 TimerThread timer(2); //Wait for 2 seconds
84 while (!timer.Finished())
108 * Sends a message to the wrapped AI program
109 * WARNING: Always prints a new line after the message (so don't include a new line)
110 * This is because everything is always line buffered.
111 * @returns true if the message was successfully sent; false if it was not (ie: the process was not running!)
113 bool Program::SendMessage(const char * print, ...)
115 if (!Running()) //Is the process running...
121 if (vfprintf(output, print, ap) < 0 || fprintf(output, "\n") < 0)
136 * Retrieves a message from the wrapped AI program, waiting a maximum amount of time
137 * @param buffer - C++ string to store the resultant message in
138 * @param timeout - Maximum amount of time to wait before failure. If timeout <= 0, then GetMessage will wait indefinately.
139 * @returns true if the response was recieved within the specified time, false if it was not, or an EOF was recieved, or the process was not running.
141 bool Program::GetMessage(string & buffer, double timeout)
146 assert(&buffer != NULL);
147 GetterThread getterThread(input, buffer);
148 assert(&(getterThread.buffer) != NULL);
149 TimerThread timerThread(timeout*1000000);
151 getterThread.Start();
156 while (!getterThread.Finished())
158 if (timeout > 0 && timerThread.Finished())
171 if (buffer.size() == 1 && buffer[0] == EOF)
179 * Returns true iff the process is running
180 * @returns what I just said, fool
182 bool Program::Running() const
184 return (pid > 0 && kill(pid,0) == 0);