Skip to content

Commit 5bbf39d

Browse files
committed
Refactor ThreadExecutor::check() to handle child failures more gracefully
1 parent 957bb5c commit 5bbf39d

3 files changed

Lines changed: 93 additions & 44 deletions

File tree

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -330,7 +330,7 @@ test/teststl.o: test/teststl.cpp lib/tokenize.h lib/checkstl.h lib/check.h lib/t
330330
test/testsuite.o: test/testsuite.cpp test/testsuite.h lib/errorlogger.h lib/settings.h test/redirect.h test/options.h
331331
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testsuite.o test/testsuite.cpp
332332

333-
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
333+
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
334334
$(CXX) $(CPPFLAGS) $(CXXFLAGS) ${INCLUDE_FOR_TEST} -c -o test/testsuppressions.o test/testsuppressions.cpp
335335

336336
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

cli/threadexecutor.cpp

Lines changed: 86 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ ThreadExecutor::ThreadExecutor(const std::vector<std::string> &filenames, Settin
3535
: _filenames(filenames), _settings(settings), _errorLogger(errorLogger), _fileCount(0)
3636
{
3737
#ifdef THREADING_MODEL_FORK
38-
_pipe[0] = _pipe[1] = 0;
38+
_wpipe = 0;
3939
#endif
4040
}
4141

@@ -55,10 +55,10 @@ void ThreadExecutor::addFileContent(const std::string &path, const std::string &
5555

5656
#ifdef THREADING_MODEL_FORK
5757

58-
int ThreadExecutor::handleRead(unsigned int &result)
58+
int ThreadExecutor::handleRead(int rpipe, unsigned int &result)
5959
{
6060
char type = 0;
61-
if (read(_pipe[0], &type, 1) <= 0)
61+
if (read(rpipe, &type, 1) <= 0)
6262
{
6363
if (errno == EAGAIN)
6464
return 0;
@@ -73,14 +73,14 @@ int ThreadExecutor::handleRead(unsigned int &result)
7373
}
7474

7575
unsigned int len = 0;
76-
if (read(_pipe[0], &len, sizeof(len)) <= 0)
76+
if (read(rpipe, &len, sizeof(len)) <= 0)
7777
{
7878
std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl;
7979
exit(0);
8080
}
8181

8282
char *buf = new char[len];
83-
if (read(_pipe[0], buf, len) <= 0)
83+
if (read(rpipe, buf, len) <= 0)
8484
{
8585
std::cerr << "#### You found a bug from cppcheck.\nThreadExecutor::handleRead error, type was:" << type << std::endl;
8686
exit(0);
@@ -134,32 +134,35 @@ unsigned int ThreadExecutor::check()
134134
{
135135
_fileCount = 0;
136136
unsigned int result = 0;
137-
if (pipe(_pipe) == -1)
138-
{
139-
perror("pipe");
140-
exit(1);
141-
}
142-
143-
int flags = 0;
144-
if ((flags = fcntl(_pipe[0], F_GETFL, 0)) < 0)
145-
{
146-
perror("fcntl");
147-
exit(1);
148-
}
149-
150-
if (fcntl(_pipe[0], F_SETFL, flags | O_NONBLOCK) < 0)
151-
{
152-
perror("fcntl");
153-
exit(1);
154-
}
155137

156-
unsigned int childCount = 0;
138+
std::list<int> rpipes;
139+
std::map<pid_t, std::string> childFile;
157140
unsigned int i = 0;
158141
while (true)
159142
{
160143
// Start a new child
161-
if (i < _filenames.size() && childCount < _settings._jobs)
144+
if (i < _filenames.size() && rpipes.size() < _settings._jobs)
162145
{
146+
int pipes[2];
147+
if (pipe(pipes) == -1)
148+
{
149+
perror("pipe");
150+
exit(1);
151+
}
152+
153+
int flags = 0;
154+
if ((flags = fcntl(pipes[0], F_GETFL, 0)) < 0)
155+
{
156+
perror("fcntl");
157+
exit(1);
158+
}
159+
160+
if (fcntl(pipes[0], F_SETFL, flags | O_NONBLOCK) < 0)
161+
{
162+
perror("fcntl");
163+
exit(1);
164+
}
165+
163166
pid_t pid = fork();
164167
if (pid < 0)
165168
{
@@ -169,6 +172,9 @@ unsigned int ThreadExecutor::check()
169172
}
170173
else if (pid == 0)
171174
{
175+
close(pipes[0]);
176+
_wpipe = pipes[1];
177+
172178
CppCheck fileChecker(*this, false);
173179
fileChecker.settings(_settings);
174180

@@ -190,31 +196,70 @@ unsigned int ThreadExecutor::check()
190196
exit(0);
191197
}
192198

193-
++childCount;
199+
close(pipes[1]);
200+
rpipes.push_back(pipes[0]);
201+
childFile[pid] = _filenames[i];
202+
194203
++i;
195204
}
196-
else if (childCount > 0)
205+
else if (!rpipes.empty())
197206
{
198-
// Wait for child to quit before stating new processes
199-
while (true)
207+
fd_set rfds;
208+
FD_ZERO(&rfds);
209+
for (std::list<int>::const_iterator rp = rpipes.begin(); rp != rpipes.end(); ++rp)
210+
FD_SET(*rp, &rfds);
211+
212+
int r = select(*std::max_element(rpipes.begin(), rpipes.end()) + 1, &rfds, NULL, NULL, NULL);
213+
214+
if (r > 0)
200215
{
201-
int readRes = handleRead(result);
202-
if (readRes == -1)
203-
break;
204-
else if (readRes == 0)
216+
std::list<int>::iterator rp = rpipes.begin();
217+
while (rp != rpipes.end())
205218
{
206-
struct timespec duration;
207-
duration.tv_sec = 0;
208-
duration.tv_nsec = 5 * 1000 * 1000; // 5 ms
209-
nanosleep(&duration, NULL);
219+
if (FD_ISSET(*rp, &rfds))
220+
{
221+
int readRes = handleRead(*rp, result);
222+
if (readRes == -1)
223+
{
224+
close(*rp);
225+
rp = rpipes.erase(rp);
226+
}
227+
else
228+
++rp;
229+
}
230+
else
231+
++rp;
210232
}
211233
}
212234

213235
int stat = 0;
214-
waitpid(0, &stat, 0);
215-
--childCount;
236+
pid_t child = waitpid(0, &stat, WNOHANG);
237+
if (child > 0)
238+
{
239+
std::string childname;
240+
std::map<pid_t, std::string>::iterator c = childFile.find(child);
241+
if (c != childFile.end())
242+
{
243+
childname = c->second;
244+
childFile.erase(c);
245+
}
246+
247+
if (WIFSIGNALED(stat))
248+
{
249+
std::ostringstream oss;
250+
oss << "Internal error: Child process crashed with signal " << WTERMSIG(stat);
251+
252+
std::list<ErrorLogger::ErrorMessage::FileLocation> locations;
253+
locations.push_back(ErrorLogger::ErrorMessage::FileLocation(childname, 0));
254+
const ErrorLogger::ErrorMessage errmsg(locations,
255+
Severity::error,
256+
oss.str(),
257+
"cppcheckError");
258+
_errorLogger.reportErr(errmsg);
259+
}
260+
}
216261
}
217-
else if (childCount == 0)
262+
else
218263
{
219264
// All done
220265
break;
@@ -232,7 +277,7 @@ void ThreadExecutor::writeToPipe(char type, const std::string &data)
232277
out[0] = type;
233278
std::memcpy(&(out[1]), &len, sizeof(len));
234279
std::memcpy(&(out[1+sizeof(len)]), data.c_str(), len);
235-
if (write(_pipe[1], out, len + 1 + sizeof(len)) <= 0)
280+
if (write(_wpipe, out, len + 1 + sizeof(len)) <= 0)
236281
{
237282
delete [] out;
238283
out = 0;

cli/threadexecutor.h

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,13 @@ class ThreadExecutor : public ErrorLogger
7171
* 0 if there is nothing in the pipe to be read
7272
* 1 if we did read something
7373
*/
74-
int handleRead(unsigned int &result);
74+
int handleRead(int rpipe, unsigned int &result);
7575
void writeToPipe(char type, const std::string &data);
76-
int _pipe[2];
76+
/**
77+
* Write end of status pipe, different for each child.
78+
* Not used in master process.
79+
*/
80+
int _wpipe;
7781
std::list<std::string> _errorList;
7882
public:
7983
/**

0 commit comments

Comments
 (0)