Use async IO in TestApplication::execApp()
rather than multiple threads
This commit is contained in:
parent
9f68a95920
commit
1a4087abbc
|
@ -9,10 +9,10 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <initializer_list>
|
#include <initializer_list>
|
||||||
#include <thread>
|
|
||||||
|
|
||||||
#ifdef PLATFORM_UNIX
|
#ifdef PLATFORM_UNIX
|
||||||
# include <unistd.h>
|
# include <unistd.h>
|
||||||
|
# include <poll.h>
|
||||||
# include <sys/wait.h>
|
# include <sys/wait.h>
|
||||||
# include <sys/stat.h>
|
# include <sys/stat.h>
|
||||||
#endif
|
#endif
|
||||||
|
@ -219,9 +219,10 @@ string TestApplication::workingCopyPath(const string &name) const
|
||||||
* \throws Throws std::runtime_error when the application can not be executed.
|
* \throws Throws std::runtime_error when the application can not be executed.
|
||||||
* \remarks
|
* \remarks
|
||||||
* - The specified \a args must be 0 terminated. The first argument is the application name.
|
* - 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
|
// print log message
|
||||||
if(!suppressLogging) {
|
if(!suppressLogging) {
|
||||||
|
@ -245,31 +246,59 @@ int TestApplication::execApp(const char *const *args, string &stdout, string &st
|
||||||
if(int child = fork()) {
|
if(int child = fork()) {
|
||||||
// parent process: read stdout and stderr from child
|
// parent process: read stdout and stderr from child
|
||||||
close(writeCoutPipe), close(writeCerrPipe);
|
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);
|
close(readCoutPipe), close(readCerrPipe);
|
||||||
throw runtime_error("Unable to create fork");
|
throw;
|
||||||
}
|
}
|
||||||
// TODO: use select instead of threads
|
|
||||||
thread readCoutThread([readCoutPipe, &stdout] {
|
// get return code
|
||||||
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();
|
|
||||||
int childReturnCode;
|
int childReturnCode;
|
||||||
waitpid(child, &childReturnCode, 0);
|
waitpid(child, &childReturnCode, 0);
|
||||||
return childReturnCode;
|
return childReturnCode;
|
||||||
|
|
|
@ -18,7 +18,7 @@ public:
|
||||||
std::string testFilePath(const std::string &name) const;
|
std::string testFilePath(const std::string &name) const;
|
||||||
#ifdef PLATFORM_UNIX
|
#ifdef PLATFORM_UNIX
|
||||||
std::string workingCopyPath(const std::string &name) const;
|
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
|
#endif
|
||||||
bool unitsSpecified() const;
|
bool unitsSpecified() const;
|
||||||
const std::vector<const char *> &units() const;
|
const std::vector<const char *> &units() const;
|
||||||
|
|
Loading…
Reference in New Issue