Use async IO in TestApplication::execApp()

rather than multiple threads
This commit is contained in:
Martchus 2016-08-15 22:35:37 +02:00
parent 9f68a95920
commit 1a4087abbc
2 changed files with 56 additions and 27 deletions

View File

@ -9,10 +9,10 @@
#include <iostream>
#include <fstream>
#include <initializer_list>
#include <thread>
#ifdef PLATFORM_UNIX
# include <unistd.h>
# include <poll.h>
# include <sys/wait.h>
# include <sys/stat.h>
#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);
try {
if(child == -1) {
close(readCoutPipe), close(readCerrPipe);
throw runtime_error("Unable to create fork");
}
// TODO: use select instead of threads
thread readCoutThread([readCoutPipe, &stdout] {
// 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();
while((count = read(readCoutPipe, buffer, sizeof(buffer))) > 0) {
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);
});
thread readCerrThread([readCerrPipe, &stderr] {
char buffer[512];
ssize_t count;
stderr.clear();
while((count = read(readCerrPipe, buffer, sizeof(buffer))) > 0) {
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);
});
readCoutThread.join();
readCerrThread.join();
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;
}
// get return code
int childReturnCode;
waitpid(child, &childReturnCode, 0);
return childReturnCode;

View File

@ -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<const char *> &units() const;