X-Git-Url: https://git.ucc.asn.au/?p=progcomp2012.git;a=blobdiff_plain;f=home%2Fprogcomp%2Fjudge%2Fmanager%2Fprogram.cpp;fp=home%2Fprogcomp%2Fjudge%2Fmanager%2Fprogram.cpp;h=660362ac06cc7535c1f2814c9e1bd6579b17c0d4;hp=0000000000000000000000000000000000000000;hb=e3b15cd5dea739f7523920d83bda592db95a7b93;hpb=7f7bc05439b70b3139086086608996de3c9ae2ed diff --git a/home/progcomp/judge/manager/program.cpp b/home/progcomp/judge/manager/program.cpp new file mode 100644 index 0000000..660362a --- /dev/null +++ b/home/progcomp/judge/manager/program.cpp @@ -0,0 +1,193 @@ +#include + +#include + +#include + +#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); +} + + + +