Refactor ThreadExecutor::check() to handle child failures more gracefully
This commit is contained in:
parent
957bb5c0f2
commit
5bbf39d094
2
Makefile
2
Makefile
|
@ -330,7 +330,7 @@ test/teststl.o: test/teststl.cpp lib/tokenize.h lib/checkstl.h lib/check.h lib/t
|
||||||
test/testsuite.o: test/testsuite.cpp test/testsuite.h lib/errorlogger.h lib/settings.h test/redirect.h test/options.h
|
test/testsuite.o: test/testsuite.cpp test/testsuite.h lib/errorlogger.h lib/settings.h test/redirect.h test/options.h
|
||||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testsuite.o test/testsuite.cpp
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testsuite.o test/testsuite.cpp
|
||||||
|
|
||||||
test/testsuppressions.o: test/testsuppressions.cpp lib/cppcheck.h lib/settings.h lib/errorlogger.h lib/checkunusedfunctions.h lib/check.h lib/token.h lib/tokenize.h test/testsuite.h test/redirect.h
|
test/testsuppressions.o: test/testsuppressions.cpp lib/cppcheck.h lib/settings.h lib/errorlogger.h lib/checkunusedfunctions.h lib/check.h cli/threadexecutor.h lib/token.h lib/tokenize.h test/testsuite.h test/redirect.h
|
||||||
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testsuppressions.o test/testsuppressions.cpp
|
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testsuppressions.o test/testsuppressions.cpp
|
||||||
|
|
||||||
test/testsymboldatabase.o: test/testsymboldatabase.cpp test/testsuite.h lib/errorlogger.h lib/settings.h test/redirect.h test/testutils.h lib/tokenize.h lib/token.h lib/symboldatabase.h
|
test/testsymboldatabase.o: test/testsymboldatabase.cpp test/testsuite.h lib/errorlogger.h lib/settings.h test/redirect.h test/testutils.h lib/tokenize.h lib/token.h lib/symboldatabase.h
|
||||||
|
|
|
@ -35,7 +35,7 @@ ThreadExecutor::ThreadExecutor(const std::vector<std::string> &filenames, Settin
|
||||||
: _filenames(filenames), _settings(settings), _errorLogger(errorLogger), _fileCount(0)
|
: _filenames(filenames), _settings(settings), _errorLogger(errorLogger), _fileCount(0)
|
||||||
{
|
{
|
||||||
#ifdef THREADING_MODEL_FORK
|
#ifdef THREADING_MODEL_FORK
|
||||||
_pipe[0] = _pipe[1] = 0;
|
_wpipe = 0;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,10 +55,10 @@ void ThreadExecutor::addFileContent(const std::string &path, const std::string &
|
||||||
|
|
||||||
#ifdef THREADING_MODEL_FORK
|
#ifdef THREADING_MODEL_FORK
|
||||||
|
|
||||||
int ThreadExecutor::handleRead(unsigned int &result)
|
int ThreadExecutor::handleRead(int rpipe, unsigned int &result)
|
||||||
{
|
{
|
||||||
char type = 0;
|
char type = 0;
|
||||||
if (read(_pipe[0], &type, 1) <= 0)
|
if (read(rpipe, &type, 1) <= 0)
|
||||||
{
|
{
|
||||||
if (errno == EAGAIN)
|
if (errno == EAGAIN)
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -73,14 +73,14 @@ int ThreadExecutor::handleRead(unsigned int &result)
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned int len = 0;
|
unsigned int len = 0;
|
||||||
if (read(_pipe[0], &len, sizeof(len)) <= 0)
|
if (read(rpipe, &len, sizeof(len)) <= 0)
|
||||||
{
|
{
|
||||||
std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl;
|
std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl;
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
char *buf = new char[len];
|
char *buf = new char[len];
|
||||||
if (read(_pipe[0], buf, len) <= 0)
|
if (read(rpipe, buf, len) <= 0)
|
||||||
{
|
{
|
||||||
std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl;
|
std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl;
|
||||||
exit(0);
|
exit(0);
|
||||||
|
@ -134,32 +134,35 @@ unsigned int ThreadExecutor::check()
|
||||||
{
|
{
|
||||||
_fileCount = 0;
|
_fileCount = 0;
|
||||||
unsigned int result = 0;
|
unsigned int result = 0;
|
||||||
if (pipe(_pipe) == -1)
|
|
||||||
{
|
|
||||||
perror("pipe");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
int flags = 0;
|
std::list<int> rpipes;
|
||||||
if ((flags = fcntl(_pipe[0], F_GETFL, 0)) < 0)
|
std::map<pid_t, std::string> childFile;
|
||||||
{
|
|
||||||
perror("fcntl");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fcntl(_pipe[0], F_SETFL, flags | O_NONBLOCK) < 0)
|
|
||||||
{
|
|
||||||
perror("fcntl");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int childCount = 0;
|
|
||||||
unsigned int i = 0;
|
unsigned int i = 0;
|
||||||
while (true)
|
while (true)
|
||||||
{
|
{
|
||||||
// Start a new child
|
// Start a new child
|
||||||
if (i < _filenames.size() && childCount < _settings._jobs)
|
if (i < _filenames.size() && rpipes.size() < _settings._jobs)
|
||||||
{
|
{
|
||||||
|
int pipes[2];
|
||||||
|
if (pipe(pipes) == -1)
|
||||||
|
{
|
||||||
|
perror("pipe");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int flags = 0;
|
||||||
|
if ((flags = fcntl(pipes[0], F_GETFL, 0)) < 0)
|
||||||
|
{
|
||||||
|
perror("fcntl");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0)
|
||||||
|
{
|
||||||
|
perror("fcntl");
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
pid_t pid = fork();
|
pid_t pid = fork();
|
||||||
if (pid < 0)
|
if (pid < 0)
|
||||||
{
|
{
|
||||||
|
@ -169,6 +172,9 @@ unsigned int ThreadExecutor::check()
|
||||||
}
|
}
|
||||||
else if (pid == 0)
|
else if (pid == 0)
|
||||||
{
|
{
|
||||||
|
close(pipes[0]);
|
||||||
|
_wpipe = pipes[1];
|
||||||
|
|
||||||
CppCheck fileChecker(*this, false);
|
CppCheck fileChecker(*this, false);
|
||||||
fileChecker.settings(_settings);
|
fileChecker.settings(_settings);
|
||||||
|
|
||||||
|
@ -190,31 +196,70 @@ unsigned int ThreadExecutor::check()
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
++childCount;
|
close(pipes[1]);
|
||||||
|
rpipes.push_back(pipes[0]);
|
||||||
|
childFile[pid] = _filenames[i];
|
||||||
|
|
||||||
++i;
|
++i;
|
||||||
}
|
}
|
||||||
else if (childCount > 0)
|
else if (!rpipes.empty())
|
||||||
{
|
{
|
||||||
// Wait for child to quit before stating new processes
|
fd_set rfds;
|
||||||
while (true)
|
FD_ZERO(&rfds);
|
||||||
|
for (std::list<int>::const_iterator rp = rpipes.begin(); rp != rpipes.end(); ++rp)
|
||||||
|
FD_SET(*rp, &rfds);
|
||||||
|
|
||||||
|
int r = select(*std::max_element(rpipes.begin(), rpipes.end()) + 1, &rfds, NULL, NULL, NULL);
|
||||||
|
|
||||||
|
if (r > 0)
|
||||||
{
|
{
|
||||||
int readRes = handleRead(result);
|
std::list<int>::iterator rp = rpipes.begin();
|
||||||
if (readRes == -1)
|
while (rp != rpipes.end())
|
||||||
break;
|
|
||||||
else if (readRes == 0)
|
|
||||||
{
|
{
|
||||||
struct timespec duration;
|
if (FD_ISSET(*rp, &rfds))
|
||||||
duration.tv_sec = 0;
|
{
|
||||||
duration.tv_nsec = 5 * 1000 * 1000; // 5 ms
|
int readRes = handleRead(*rp, result);
|
||||||
nanosleep(&duration, NULL);
|
if (readRes == -1)
|
||||||
|
{
|
||||||
|
close(*rp);
|
||||||
|
rp = rpipes.erase(rp);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++rp;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
++rp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int stat = 0;
|
int stat = 0;
|
||||||
waitpid(0, &stat, 0);
|
pid_t child = waitpid(0, &stat, WNOHANG);
|
||||||
--childCount;
|
if (child > 0)
|
||||||
|
{
|
||||||
|
std::string childname;
|
||||||
|
std::map<pid_t, std::string>::iterator c = childFile.find(child);
|
||||||
|
if (c != childFile.end())
|
||||||
|
{
|
||||||
|
childname = c->second;
|
||||||
|
childFile.erase(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WIFSIGNALED(stat))
|
||||||
|
{
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "Internal error: Child process crashed with signal " << WTERMSIG(stat);
|
||||||
|
|
||||||
|
std::list<ErrorLogger::ErrorMessage::FileLocation> locations;
|
||||||
|
locations.push_back(ErrorLogger::ErrorMessage::FileLocation(childname, 0));
|
||||||
|
const ErrorLogger::ErrorMessage errmsg(locations,
|
||||||
|
Severity::error,
|
||||||
|
oss.str(),
|
||||||
|
"cppcheckError");
|
||||||
|
_errorLogger.reportErr(errmsg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (childCount == 0)
|
else
|
||||||
{
|
{
|
||||||
// All done
|
// All done
|
||||||
break;
|
break;
|
||||||
|
@ -232,7 +277,7 @@ void ThreadExecutor::writeToPipe(char type, const std::string &data)
|
||||||
out[0] = type;
|
out[0] = type;
|
||||||
std::memcpy(&(out[1]), &len, sizeof(len));
|
std::memcpy(&(out[1]), &len, sizeof(len));
|
||||||
std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len);
|
std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len);
|
||||||
if (write(_pipe[1], out, len + 1 + sizeof(len)) <= 0)
|
if (write(_wpipe, out, len + 1 + sizeof(len)) <= 0)
|
||||||
{
|
{
|
||||||
delete [] out;
|
delete [] out;
|
||||||
out = 0;
|
out = 0;
|
||||||
|
|
|
@ -71,9 +71,13 @@ private:
|
||||||
* 0 if there is nothing in the pipe to be read
|
* 0 if there is nothing in the pipe to be read
|
||||||
* 1 if we did read something
|
* 1 if we did read something
|
||||||
*/
|
*/
|
||||||
int handleRead(unsigned int &result);
|
int handleRead(int rpipe, unsigned int &result);
|
||||||
void writeToPipe(char type, const std::string &data);
|
void writeToPipe(char type, const std::string &data);
|
||||||
int _pipe[2];
|
/**
|
||||||
|
* Write end of status pipe, different for each child.
|
||||||
|
* Not used in master process.
|
||||||
|
*/
|
||||||
|
int _wpipe;
|
||||||
std::list<std::string> _errorList;
|
std::list<std::string> _errorList;
|
||||||
public:
|
public:
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue