From 1a4087abbc3c2f72775f05c7a2d180ccebbd6dd2 Mon Sep 17 00:00:00 2001 From: Martchus Date: Mon, 15 Aug 2016 22:35:37 +0200 Subject: [PATCH] Use async IO in TestApplication::execApp() rather than multiple threads --- tests/testutils.cpp | 81 ++++++++++++++++++++++++++++++--------------- tests/testutils.h | 2 +- 2 files changed, 56 insertions(+), 27 deletions(-) diff --git a/tests/testutils.cpp b/tests/testutils.cpp index ffc8bb4..35ceb12 100644 --- a/tests/testutils.cpp +++ b/tests/testutils.cpp @@ -9,10 +9,10 @@ #include #include #include -#include #ifdef PLATFORM_UNIX # include +# include # include # include #endif @@ -219,9 +219,10 @@ string TestApplication::workingCopyPath(const string &name) const * \throws Throws std::runtime_error when the application can not be executed. * \remarks * - The specified \a args must be 0 terminated. The first argument is the application name. - * - Currently only available under UNIX. + * - Currently only supported under UNIX. + * - \a stdout and \a stderr are cleared before. */ -int TestApplication::execApp(const char *const *args, string &stdout, string &stderr, bool suppressLogging) const +int TestApplication::execApp(const char *const *args, string &stdout, string &stderr, bool suppressLogging, int timeout) const { // print log message if(!suppressLogging) { @@ -245,31 +246,59 @@ int TestApplication::execApp(const char *const *args, string &stdout, string &st if(int child = fork()) { // parent process: read stdout and stderr from child close(writeCoutPipe), close(writeCerrPipe); - if(child == -1) { + + try { + if(child == -1) { + throw runtime_error("Unable to create fork"); + } + + // init file descriptor set for poll + struct pollfd fileDescriptorSet[2]; + fileDescriptorSet[0].fd = readCoutPipe; + fileDescriptorSet[1].fd = readCerrPipe; + fileDescriptorSet[0].events = fileDescriptorSet[1].events = POLLIN; + + // init variables for reading + char buffer[512]; + ssize_t count; + stdout.clear(), stderr.clear(); + + // poll as long as at least one pipe is open + do { + int retpoll = poll(fileDescriptorSet, 2, timeout); + if(retpoll > 0) { + // poll succeeds + if(fileDescriptorSet[0].revents & POLLIN) { + if((count = read(readCoutPipe, buffer, sizeof(buffer))) > 0) { + stdout.append(buffer, count); + } + } else if(fileDescriptorSet[0].revents & POLLHUP) { + close(readCoutPipe); + fileDescriptorSet[0].fd = -1; + } + if(fileDescriptorSet[1].revents & POLLIN) { + if((count = read(readCerrPipe, buffer, sizeof(buffer))) > 0) { + stderr.append(buffer, count); + } + } else if(fileDescriptorSet[1].revents & POLLHUP) { + close(readCerrPipe); + fileDescriptorSet[1].fd = -1; + } + } else if(retpoll == 0) { + // timeout + throw runtime_error("Poll time-out"); + } else { + // fail + throw runtime_error("Poll failed"); + } + } while(fileDescriptorSet[0].fd >= 0 || fileDescriptorSet[1].fd >= 0); + } catch(...) { + // ensure all pipes are close in the error case close(readCoutPipe), close(readCerrPipe); - throw runtime_error("Unable to create fork"); + throw; } - // TODO: use select instead of threads - thread readCoutThread([readCoutPipe, &stdout] { - char buffer[512]; - ssize_t count; - stdout.clear(); - while((count = read(readCoutPipe, buffer, sizeof(buffer))) > 0) { - stdout.append(buffer, count); - } - close(readCoutPipe); - }); - thread readCerrThread([readCerrPipe, &stderr] { - char buffer[512]; - ssize_t count; - stderr.clear(); - while((count = read(readCerrPipe, buffer, sizeof(buffer))) > 0) { - stderr.append(buffer, count); - } - close(readCerrPipe); - }); - readCoutThread.join(); - readCerrThread.join(); + + // get return code int childReturnCode; waitpid(child, &childReturnCode, 0); return childReturnCode; diff --git a/tests/testutils.h b/tests/testutils.h index 099a7f3..ff272c8 100644 --- a/tests/testutils.h +++ b/tests/testutils.h @@ -18,7 +18,7 @@ public: std::string testFilePath(const std::string &name) const; #ifdef PLATFORM_UNIX std::string workingCopyPath(const std::string &name) const; - int execApp(const char *const *args, std::string &output, std::string &errors, bool suppressLogging = false) const; + int execApp(const char *const *args, std::string &output, std::string &errors, bool suppressLogging = false, int timeout = -1) const; #endif bool unitsSpecified() const; const std::vector &units() const;