Skip to content

Commit a00b6e1

Browse files
authored
simplified ThreadExecutor class by moving some code out of it / fixed some thread safety issues (#4849)
1 parent 9291421 commit a00b6e1

12 files changed

+221
-122
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -643,7 +643,7 @@ cli/cppcheckexecutorseh.o: cli/cppcheckexecutorseh.cpp cli/cppcheckexecutor.h cl
643643
cli/cppcheckexecutorsig.o: cli/cppcheckexecutorsig.cpp cli/cppcheckexecutor.h cli/cppcheckexecutorsig.h cli/stacktrace.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/suppressions.h
644644
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/cppcheckexecutorsig.cpp
645645

646-
cli/executor.o: cli/executor.cpp cli/executor.h
646+
cli/executor.o: cli/executor.cpp cli/executor.h lib/color.h lib/config.h lib/errorlogger.h lib/errortypes.h lib/importproject.h lib/library.h lib/mathlib.h lib/platform.h lib/settings.h lib/standards.h lib/suppressions.h lib/timer.h lib/utils.h
647647
$(CXX) ${INCLUDE_FOR_CLI} $(CPPFLAGS) $(CXXFLAGS) -c -o $@ cli/executor.cpp
648648

649649
cli/filelister.o: cli/filelister.cpp cli/filelister.h lib/config.h lib/path.h lib/pathmatch.h lib/utils.h

cli/cppcheckexecutor.cpp

+2
Original file line numberDiff line numberDiff line change
@@ -345,6 +345,7 @@ int CppCheckExecutor::check_internal(CppCheck& cppcheck)
345345
}
346346
}
347347

348+
// TODO: not performed when multiple jobs are being used
348349
// second loop to parse all markup files which may not work until all
349350
// c/cpp files have been parsed and checked
350351
for (std::map<std::string, std::size_t>::const_iterator i = mFiles.cbegin(); i != mFiles.cend(); ++i) {
@@ -462,6 +463,7 @@ void CppCheckExecutor::reportStatus(std::size_t fileindex, std::size_t filecount
462463
oss << fileindex << '/' << filecount
463464
<< " files checked " << percentDone
464465
<< "% done";
466+
// TODO: do not unconditionally print in color
465467
std::cout << Color::FgBlue << oss.str() << Color::Reset << std::endl;
466468
}
467469
}

cli/executor.cpp

+20
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,30 @@
1818

1919
#include "executor.h"
2020

21+
#include "errorlogger.h"
22+
#include "settings.h"
23+
24+
#include <algorithm>
25+
2126
Executor::Executor(const std::map<std::string, std::size_t> &files, Settings &settings, ErrorLogger &errorLogger)
2227
: mFiles(files), mSettings(settings), mErrorLogger(errorLogger)
2328
{}
2429

2530
Executor::~Executor()
2631
{}
2732

33+
bool Executor::hasToLog(const ErrorMessage &msg)
34+
{
35+
if (!mSettings.nomsg.isSuppressed(msg))
36+
{
37+
std::string errmsg = msg.toString(mSettings.verbose);
38+
39+
std::lock_guard<std::mutex> lg(mErrorListSync);
40+
if (std::find(mErrorList.cbegin(), mErrorList.cend(), errmsg) == mErrorList.cend()) {
41+
mErrorList.emplace_back(std::move(errmsg));
42+
return true;
43+
}
44+
}
45+
return false;
46+
}
47+

cli/executor.h

+12
Original file line numberDiff line numberDiff line change
@@ -22,10 +22,12 @@
2222
#include <cstddef>
2323
#include <list>
2424
#include <map>
25+
#include <mutex>
2526
#include <string>
2627

2728
class Settings;
2829
class ErrorLogger;
30+
class ErrorMessage;
2931

3032
/// @addtogroup CLI
3133
/// @{
@@ -45,9 +47,19 @@ class Executor {
4547
virtual unsigned int check() = 0;
4648

4749
protected:
50+
/**
51+
* @brief Check if message is being suppressed and unique.
52+
* @param msg the message to check
53+
* @return true if message is not suppressed and unique
54+
*/
55+
bool hasToLog(const ErrorMessage &msg);
56+
4857
const std::map<std::string, std::size_t> &mFiles;
4958
Settings &mSettings;
5059
ErrorLogger &mErrorLogger;
60+
61+
private:
62+
std::mutex mErrorListSync;
5163
std::list<std::string> mErrorList;
5264
};
5365

cli/processexecutor.cpp

+5-10
Original file line numberDiff line numberDiff line change
@@ -169,16 +169,11 @@ int ProcessExecutor::handleRead(int rpipe, unsigned int &result)
169169
std::exit(EXIT_FAILURE);
170170
}
171171

172-
if (!mSettings.nomsg.isSuppressed(msg)) {
173-
// Alert only about unique errors
174-
std::string errmsg = msg.toString(mSettings.verbose);
175-
if (std::find(mErrorList.cbegin(), mErrorList.cend(), errmsg) == mErrorList.cend()) {
176-
mErrorList.emplace_back(std::move(errmsg));
177-
if (type == PipeWriter::REPORT_ERROR)
178-
mErrorLogger.reportErr(msg);
179-
else
180-
mErrorLogger.reportInfo(msg);
181-
}
172+
if (hasToLog(msg)) {
173+
if (type == PipeWriter::REPORT_ERROR)
174+
mErrorLogger.reportErr(msg);
175+
else
176+
mErrorLogger.reportInfo(msg);
182177
}
183178
} else if (type == PipeWriter::CHILD_END) {
184179
std::istringstream iss(buf);

cli/threadexecutor.cpp

+106-94
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "threadexecutor.h"
2020

2121
#include "color.h"
22+
#include "config.h"
2223
#include "cppcheck.h"
2324
#include "cppcheckexecutor.h"
2425
#include "errorlogger.h"
@@ -45,97 +46,158 @@ ThreadExecutor::ThreadExecutor(const std::map<std::string, std::size_t> &files,
4546
ThreadExecutor::~ThreadExecutor()
4647
{}
4748

48-
class ThreadExecutor::SyncLogForwarder : public ErrorLogger
49+
class Data
4950
{
5051
public:
51-
explicit SyncLogForwarder(ThreadExecutor &threadExecutor)
52-
: mThreadExecutor(threadExecutor), mProcessedFiles(0), mTotalFiles(0), mProcessedSize(0) {
53-
54-
const std::map<std::string, std::size_t>& files = mThreadExecutor.mFiles;
55-
mItNextFile = files.begin();
56-
mItNextFileSettings = mThreadExecutor.mSettings.project.fileSettings.begin();
52+
Data(const std::map<std::string, std::size_t> &files, const std::list<ImportProject::FileSettings> &fileSettings)
53+
: mFiles(files), mFileSettings(fileSettings), mProcessedFiles(0), mProcessedSize(0)
54+
{
55+
mItNextFile = mFiles.begin();
56+
mItNextFileSettings = mFileSettings.begin();
5757

58-
mTotalFiles = files.size() + mThreadExecutor.mSettings.project.fileSettings.size();
59-
mTotalFileSize = std::accumulate(files.cbegin(), files.cend(), std::size_t(0), [](std::size_t v, const std::pair<std::string, std::size_t>& p) {
58+
mTotalFiles = mFiles.size() + mFileSettings.size();
59+
mTotalFileSize = std::accumulate(mFiles.cbegin(), mFiles.cend(), std::size_t(0), [](std::size_t v, const std::pair<std::string, std::size_t>& p) {
6060
return v + p.second;
6161
});
6262
}
6363

64-
void reportOut(const std::string &outmsg, Color c) override
65-
{
66-
std::lock_guard<std::mutex> lg(mReportSync);
67-
68-
mThreadExecutor.mErrorLogger.reportOut(outmsg, c);
64+
bool finished() {
65+
std::lock_guard<std::mutex> l(mFileSync);
66+
return mItNextFile == mFiles.cend() && mItNextFileSettings == mFileSettings.cend();
6967
}
7068

71-
void reportErr(const ErrorMessage &msg) override {
72-
report(msg, MessageType::REPORT_ERROR);
73-
}
69+
bool next(const std::string *&file, const ImportProject::FileSettings *&fs, std::size_t &fileSize) {
70+
std::lock_guard<std::mutex> l(mFileSync);
71+
if (mItNextFile != mFiles.end()) {
72+
file = &mItNextFile->first;
73+
fileSize = mItNextFile->second;
74+
++mItNextFile;
75+
return true;
76+
}
77+
if (mItNextFileSettings != mFileSettings.end()) {
78+
fs = &(*mItNextFileSettings);
79+
fileSize = 0;
80+
++mItNextFileSettings;
81+
return true;
82+
}
7483

75-
void reportInfo(const ErrorMessage &msg) override {
76-
report(msg, MessageType::REPORT_INFO);
84+
return false;
7785
}
7886

79-
ThreadExecutor &mThreadExecutor;
80-
87+
private:
88+
const std::map<std::string, std::size_t> &mFiles;
8189
std::map<std::string, std::size_t>::const_iterator mItNextFile;
90+
const std::list<ImportProject::FileSettings> &mFileSettings;
8291
std::list<ImportProject::FileSettings>::const_iterator mItNextFileSettings;
8392

93+
public:
8494
std::size_t mProcessedFiles;
8595
std::size_t mTotalFiles;
8696
std::size_t mProcessedSize;
8797
std::size_t mTotalFileSize;
8898

8999
std::mutex mFileSync;
90-
std::mutex mErrorSync;
100+
};
101+
102+
class SyncLogForwarder : public ErrorLogger
103+
{
104+
public:
105+
explicit SyncLogForwarder(ThreadExecutor &threadExecutor, ErrorLogger &errorLogger)
106+
: mThreadExecutor(threadExecutor), mErrorLogger(errorLogger) {}
107+
108+
void reportOut(const std::string &outmsg, Color c) override
109+
{
110+
std::lock_guard<std::mutex> lg(mReportSync);
111+
112+
mErrorLogger.reportOut(outmsg, c);
113+
}
114+
115+
void reportErr(const ErrorMessage &msg) override {
116+
report(msg, MessageType::REPORT_ERROR);
117+
}
118+
119+
void reportInfo(const ErrorMessage &msg) override {
120+
report(msg, MessageType::REPORT_INFO);
121+
}
122+
91123
std::mutex mReportSync;
92124

93125
private:
94126
enum class MessageType {REPORT_ERROR, REPORT_INFO};
95127

96128
void report(const ErrorMessage &msg, MessageType msgType)
97129
{
98-
if (mThreadExecutor.mSettings.nomsg.isSuppressed(msg))
130+
if (!mThreadExecutor.hasToLog(msg))
99131
return;
100132

101-
// Alert only about unique errors
102-
bool reportError = false;
133+
std::lock_guard<std::mutex> lg(mReportSync);
103134

104-
{
105-
std::string errmsg = msg.toString(mThreadExecutor.mSettings.verbose);
135+
switch (msgType) {
136+
case MessageType::REPORT_ERROR:
137+
mErrorLogger.reportErr(msg);
138+
break;
139+
case MessageType::REPORT_INFO:
140+
mErrorLogger.reportInfo(msg);
141+
break;
142+
}
143+
}
106144

107-
std::lock_guard<std::mutex> lg(mErrorSync);
108-
if (std::find(mThreadExecutor.mErrorList.cbegin(), mThreadExecutor.mErrorList.cend(), errmsg) == mThreadExecutor.mErrorList.cend()) {
109-
mThreadExecutor.mErrorList.emplace_back(std::move(errmsg));
110-
reportError = true;
111-
}
145+
ThreadExecutor &mThreadExecutor;
146+
ErrorLogger &mErrorLogger;
147+
};
148+
149+
static unsigned int STDCALL threadProc(Data *data, SyncLogForwarder* logForwarder, const Settings &settings)
150+
{
151+
unsigned int result = 0;
152+
153+
for (;;) {
154+
if (data->finished()) {
155+
break;
112156
}
113157

114-
if (reportError) {
115-
std::lock_guard<std::mutex> lg(mReportSync);
158+
const std::string *file = nullptr;
159+
const ImportProject::FileSettings *fs = nullptr;
160+
std::size_t fileSize;
161+
if (!data->next(file, fs, fileSize))
162+
break;
116163

117-
switch (msgType) {
118-
case MessageType::REPORT_ERROR:
119-
mThreadExecutor.mErrorLogger.reportErr(msg);
120-
break;
121-
case MessageType::REPORT_INFO:
122-
mThreadExecutor.mErrorLogger.reportInfo(msg);
123-
break;
164+
CppCheck fileChecker(*logForwarder, false, CppCheckExecutor::executeCommand);
165+
fileChecker.settings() = settings;
166+
167+
if (fs) {
168+
// file settings..
169+
result += fileChecker.check(*fs);
170+
if (settings.clangTidy)
171+
fileChecker.analyseClangTidy(*fs);
172+
} else {
173+
// Read file from a file
174+
result += fileChecker.check(*file);
175+
}
176+
177+
{
178+
std::lock_guard<std::mutex> l(data->mFileSync);
179+
data->mProcessedSize += fileSize;
180+
data->mProcessedFiles++;
181+
if (!settings.quiet) {
182+
std::lock_guard<std::mutex> lg(logForwarder->mReportSync);
183+
CppCheckExecutor::reportStatus(data->mProcessedFiles, data->mTotalFiles, data->mProcessedSize, data->mTotalFileSize);
124184
}
125185
}
126186
}
127-
};
187+
return result;
188+
}
128189

129190
unsigned int ThreadExecutor::check()
130191
{
131192
std::vector<std::future<unsigned int>> threadFutures;
132193
threadFutures.reserve(mSettings.jobs);
133194

134-
SyncLogForwarder logforwarder(*this);
195+
Data data(mFiles, mSettings.project.fileSettings);
196+
SyncLogForwarder logforwarder(*this, mErrorLogger);
135197

136198
for (unsigned int i = 0; i < mSettings.jobs; ++i) {
137199
try {
138-
threadFutures.emplace_back(std::async(std::launch::async, threadProc, &logforwarder));
200+
threadFutures.emplace_back(std::async(std::launch::async, &threadProc, &data, &logforwarder, mSettings));
139201
}
140202
catch (const std::system_error &e) {
141203
std::cerr << "#### ThreadExecutor::check exception :" << e.what() << std::endl;
@@ -147,53 +209,3 @@ unsigned int ThreadExecutor::check()
147209
return v + f.get();
148210
});
149211
}
150-
151-
unsigned int STDCALL ThreadExecutor::threadProc(SyncLogForwarder* logForwarder)
152-
{
153-
unsigned int result = 0;
154-
155-
std::map<std::string, std::size_t>::const_iterator &itFile = logForwarder->mItNextFile;
156-
std::list<ImportProject::FileSettings>::const_iterator &itFileSettings = logForwarder->mItNextFileSettings;
157-
158-
// guard static members of CppCheck against concurrent access
159-
logForwarder->mFileSync.lock();
160-
161-
for (;;) {
162-
if (itFile == logForwarder->mThreadExecutor.mFiles.cend() && itFileSettings == logForwarder->mThreadExecutor.mSettings.project.fileSettings.cend()) {
163-
logForwarder->mFileSync.unlock();
164-
break;
165-
}
166-
167-
CppCheck fileChecker(*logForwarder, false, CppCheckExecutor::executeCommand);
168-
fileChecker.settings() = logForwarder->mThreadExecutor.mSettings;
169-
170-
std::size_t fileSize = 0;
171-
if (itFile != logForwarder->mThreadExecutor.mFiles.end()) {
172-
const std::string &file = itFile->first;
173-
fileSize = itFile->second;
174-
++itFile;
175-
176-
logForwarder->mFileSync.unlock();
177-
178-
// Read file from a file
179-
result += fileChecker.check(file);
180-
} else { // file settings..
181-
const ImportProject::FileSettings &fs = *itFileSettings;
182-
++itFileSettings;
183-
logForwarder->mFileSync.unlock();
184-
result += fileChecker.check(fs);
185-
if (logForwarder->mThreadExecutor.mSettings.clangTidy)
186-
fileChecker.analyseClangTidy(fs);
187-
}
188-
189-
logForwarder->mFileSync.lock();
190-
191-
logForwarder->mProcessedSize += fileSize;
192-
logForwarder->mProcessedFiles++;
193-
if (!logForwarder->mThreadExecutor.mSettings.quiet) {
194-
std::lock_guard<std::mutex> lg(logForwarder->mReportSync);
195-
CppCheckExecutor::reportStatus(logForwarder->mProcessedFiles, logForwarder->mTotalFiles, logForwarder->mProcessedSize, logForwarder->mTotalFileSize);
196-
}
197-
}
198-
return result;
199-
}

cli/threadexecutor.h

+1-5
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,6 @@
1919
#ifndef THREADEXECUTOR_H
2020
#define THREADEXECUTOR_H
2121

22-
#include "config.h"
23-
2422
#include "executor.h"
2523

2624
#include <cstddef>
@@ -46,9 +44,7 @@ class ThreadExecutor : public Executor {
4644

4745
unsigned int check() override;
4846

49-
private:
50-
class SyncLogForwarder;
51-
static unsigned int STDCALL threadProc(SyncLogForwarder *logForwarder);
47+
friend class SyncLogForwarder;
5248
};
5349

5450
/// @}

0 commit comments

Comments
 (0)