The /home was not necessary
[progcomp2012.git] / progcomp / judge / manager / program.cpp
diff --git a/progcomp/judge/manager/program.cpp b/progcomp/judge/manager/program.cpp
new file mode 100644 (file)
index 0000000..660362a
--- /dev/null
@@ -0,0 +1,193 @@
+#include <sstream>
+
+#include <stdarg.h>
+
+#include <cassert>
+
+#include "thread_util.h"
+#include "program.h"
+
+
+using namespace std;
+
+
+/**
+ * Constructor
+ * @param executablePath - path to the program that will be run
+ *
+ * Creates two pipes - one for each direction between the parent process and the AI program
+ * Forks the process. 
+ *     The child process closes unused sides of the pipe, and then calls exec to replace itself with the AI program
+ *     The parent process closes unused sides of the pipe, and sets up member variables - associates streams with the pipe fd's for convenience.
+ */
+Program::Program(const char * executablePath) : input(NULL), output(NULL), pid(0)
+{
+       //See if file exists...
+       FILE * file = fopen(executablePath, "r");
+       if (file != NULL)
+       {
+               fclose(file);
+       }
+       else
+       {
+               pid = -1;
+               return;
+       }
+       
+       int readPipe[2]; int writePipe[2];
+       assert(pipe(readPipe) == 0);
+       assert(pipe(writePipe) == 0);
+
+       pid = fork();
+       if (pid == 0)
+       {
+               close(readPipe[0]);  //close end that parent reads from
+               close(writePipe[1]); //close end that parent writes to
+
+               //TODO: Fix possible bug here if the process is already a daemon
+               assert(writePipe[0] != 0 && readPipe[1] != 1);
+               dup2(writePipe[0],0); close(writePipe[0]); //pipe end child reads from goes to STDIN
+               dup2(readPipe[1], 1); close(readPipe[1]); //pipe end child writes to goes to STDOUT
+
+               //TODO: Somehow force the exec'd process to be unbuffered
+               setbuf(stdin, NULL); //WARNING: These lines don't appear to have any affect
+               setbuf(stdout, NULL); //You should add them at the start of the wrapped program.
+                                       //If your wrapped program is not written in C/C++, you will probably have a problem
+                               
+
+
+               execl(executablePath, executablePath, (char*)(NULL)); ///Replace process with desired executable
+               //fprintf(stderr, "Program::Program - Could not run program \"%s\"!\n", executablePath);
+               //exit(EXIT_FAILURE); //We will probably have to terminate the whole program if this happens
+       }
+       else
+       {
+               close(writePipe[0]); //close end that child writes to
+               close(readPipe[1]); //close end that child reads from
+
+               input = fdopen(readPipe[0],"r"); output = fdopen(writePipe[1],"w");
+               setbuf(input, NULL);
+               setbuf(output, NULL);
+       }
+       
+}
+
+/**
+ * Destructor
+ * Writes EOF to the wrapped program and then closes all streams
+ * Kills the wrapped program if it does not exit within 1 second.
+ */
+Program::~Program()
+{
+       if (Running()) //Check if the process created is still running...
+       {
+               //fputc(EOF, output); //If it was, tell it to stop with EOF
+
+               TimerThread timer(2); //Wait for 2 seconds
+               timer.Start();          
+               while (!timer.Finished())
+               {
+                       if (!Running())
+                       {
+                               timer.Stop();
+                               break;
+                       }
+               }
+               timer.Stop();
+               kill(pid, SIGKILL);
+       }
+       if (pid > 0)
+       {
+               fclose(input);
+               fclose(output);
+       }
+       
+}
+
+
+
+
+
+/**
+ * Sends a message to the wrapped AI program
+ * WARNING: Always prints a new line after the message (so don't include a new line)
+ *     This is because everything is always line buffered.
+ * @returns true if the message was successfully sent; false if it was not (ie: the process was not running!)
+ */
+bool Program::SendMessage(const char * print, ...)
+{
+       if (!Running()) //Is the process running...
+               return false; 
+
+       va_list ap;
+       va_start(ap, print);
+
+       if (vfprintf(output, print, ap) < 0 || fprintf(output, "\n") < 0)
+       {
+               va_end(ap);
+               return false;
+       }
+       va_end(ap);
+       
+
+
+
+       return true;
+}
+
+
+/**
+ * Retrieves a message from the wrapped AI program, waiting a maximum amount of time
+ * @param buffer - C++ string to store the resultant message in
+ * @param timeout - Maximum amount of time to wait before failure. If timeout <= 0, then GetMessage will wait indefinately.
+ * @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.
+ */
+bool Program::GetMessage(string & buffer, double timeout)
+{
+       if (!Running())
+               return false;
+
+       assert(&buffer != NULL);
+       GetterThread getterThread(input, buffer);
+       assert(&(getterThread.buffer) != NULL);
+       TimerThread timerThread(timeout*1000000);
+
+       getterThread.Start();
+       if (timeout > 0)
+               timerThread.Start();
+
+       
+       while (!getterThread.Finished())
+       {
+               if (timeout > 0 && timerThread.Finished())
+               {
+                       getterThread.Stop();
+                       timerThread.Stop();
+                       return false;
+               }
+       }
+
+       getterThread.Stop();
+       timerThread.Stop();
+
+       
+
+       if (buffer.size() == 1 && buffer[0] == EOF)
+               return false;
+       return true;
+
+
+}
+
+/**
+ * Returns true iff the process is running
+ * @returns what I just said, fool
+ */
+bool Program::Running() const
+{
+       return (pid > 0 && kill(pid,0) == 0);
+}
+
+
+
+

UCC git Repository :: git.ucc.asn.au