Clang: Do not use Core::MessageManager from worker thread

...since that's not thread-safe and thus leads to crashes from time to
time.

Change-Id: I907ae8b5c4d60bfc82bc97b55cc5d62d8bbeae04
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
This commit is contained in:
Nikolai Kosjar
2014-03-04 15:01:57 -03:00
parent cb66d1ec23
commit a723205f6c
2 changed files with 192 additions and 166 deletions

View File

@@ -34,6 +34,7 @@
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <utils/qtcassert.h>
#include <utils/runextensions.h> #include <utils/runextensions.h>
#include <QFile> #include <QFile>
@@ -153,14 +154,36 @@ void PchManager::updatePchInfo(ClangProjectSettings *cps,
const QList<ProjectPart::Ptr> &projectParts) const QList<ProjectPart::Ptr> &projectParts)
{ {
if (m_pchGenerationWatcher.isRunning()) { if (m_pchGenerationWatcher.isRunning()) {
// m_pchGenerationWatcher.cancel();
m_pchGenerationWatcher.waitForFinished(); m_pchGenerationWatcher.waitForFinished();
} }
QFuture<void> future = QtConcurrent::run(&PchManager::doPchInfoUpdate, const QString customPchFile = cps->customPchFile();
cps->pchUsage(), const ClangProjectSettings::PchUsage pchUsage = cps->pchUsage();
cps->customPchFile(),
projectParts); void (*updateFunction)(QFutureInterface<void> &future,
const PchManager::UpdateParams &params) = 0;
QString message;
if (pchUsage == ClangProjectSettings::PchUse_None
|| (pchUsage == ClangProjectSettings::PchUse_Custom && customPchFile.isEmpty())) {
updateFunction = &PchManager::doPchInfoUpdateNone;
message = QLatin1String("updatePchInfo: switching to none");
} else if (pchUsage == ClangProjectSettings::PchUse_BuildSystem_Fuzzy) {
updateFunction = &PchManager::doPchInfoUpdateFuzzy;
message = QLatin1String("updatePchInfo: switching to build system (fuzzy)");
} else if (pchUsage == ClangProjectSettings::PchUse_BuildSystem_Exact) {
updateFunction = &PchManager::doPchInfoUpdateExact;
message = QLatin1String("updatePchInfo: switching to build system (exact)");
} else if (pchUsage == ClangProjectSettings::PchUse_Custom) {
updateFunction = &PchManager::doPchInfoUpdateCustom;
message = QLatin1String("updatePchInfo: switching to custom") + customPchFile;
}
QTC_ASSERT(updateFunction && !message.isEmpty(), return);
Core::MessageManager::write(message, Core::MessageManager::Silent);
const UpdateParams updateParams = UpdateParams(customPchFile, projectParts);
QFuture<void> future
= QtConcurrent::run<void, const UpdateParams &>(updateFunction, updateParams);
m_pchGenerationWatcher.setFuture(future); m_pchGenerationWatcher.setFuture(future);
Core::ProgressManager::addTask(future, tr("Precompiling..."), "Key.Tmp.Precompiling"); Core::ProgressManager::addTask(future, tr("Precompiling..."), "Key.Tmp.Precompiling");
} }
@@ -214,183 +237,177 @@ CppTools::ProjectFile::Kind getPrefixFileKind(bool hasObjectiveC, bool hasCPlusP
} }
void PchManager::doPchInfoUpdate(QFutureInterface<void> &future, void PchManager::doPchInfoUpdateNone(QFutureInterface<void> &future, const PchManager::UpdateParams &params)
ClangProjectSettings::PchUsage pchUsage,
const QString customPchFile,
const QList<ProjectPart::Ptr> projectParts)
{ {
PchManager *pchManager = PchManager::instance(); future.setProgressRange(0, 1);
PchInfo::Ptr emptyPch = PchInfo::createEmpty();
PchManager::instance()->setPCHInfo(params.projectParts, emptyPch,
qMakePair(true, QStringList()));
future.setProgressValue(1);
}
// qDebug() << "switching to" << pchUsage; void PchManager::doPchInfoUpdateFuzzy(QFutureInterface<void> &future, const PchManager::UpdateParams &params)
{
QHash<QString, QSet<QString> > includes, frameworks;
QHash<QString, QSet<QByteArray> > definesPerPCH;
QHash<QString, bool> objc;
QHash<QString, bool> cplusplus;
QHash<QString, ProjectPart::QtVersion> qtVersions;
QHash<QString, ProjectPart::CVersion> cVersions;
QHash<QString, ProjectPart::CXXVersion> cxxVersions;
QHash<QString, ProjectPart::CXXExtensions> cxxExtensionsMap;
QHash<QString, QList<ProjectPart::Ptr> > inputToParts;
foreach (const ProjectPart::Ptr &projectPart, params.projectParts) {
if (projectPart->precompiledHeaders.isEmpty())
continue;
const QString &pch = projectPart->precompiledHeaders.first(); // TODO: support more than 1 PCH file.
if (!QFile(pch).exists())
continue;
inputToParts[pch].append(projectPart);
if (pchUsage == ClangProjectSettings::PchUse_None includes[pch].unite(QSet<QString>::fromList(projectPart->includePaths));
|| (pchUsage == ClangProjectSettings::PchUse_Custom && customPchFile.isEmpty())) { frameworks[pch].unite(QSet<QString>::fromList(projectPart->frameworkPaths));
future.setProgressRange(0, 2); cVersions[pch] = std::max(cVersions.value(pch, ProjectPart::C89), projectPart->cVersion);
Core::MessageManager::write(QLatin1String("updatePchInfo: switching to none"), cxxVersions[pch] = std::max(cxxVersions.value(pch, ProjectPart::CXX98), projectPart->cxxVersion);
Core::MessageManager::Silent); cxxExtensionsMap[pch] = cxxExtensionsMap[pch] | projectPart->cxxExtensions;
PchInfo::Ptr emptyPch = PchInfo::createEmpty();
pchManager->setPCHInfo(projectParts, emptyPch, qMakePair(true, QStringList()));
future.setProgressValue(1);
} else if (pchUsage == ClangProjectSettings::PchUse_BuildSystem_Fuzzy) {
Core::MessageManager::write(
QLatin1String("updatePchInfo: switching to build system (fuzzy)"),
Core::MessageManager::Silent);
QHash<QString, QSet<QString> > includes, frameworks;
QHash<QString, QSet<QByteArray> > definesPerPCH;
QHash<QString, bool> objc;
QHash<QString, bool> cplusplus;
QHash<QString, ProjectPart::QtVersion> qtVersions;
QHash<QString, ProjectPart::CVersion> cVersions;
QHash<QString, ProjectPart::CXXVersion> cxxVersions;
QHash<QString, ProjectPart::CXXExtensions> cxxExtensionsMap;
QHash<QString, QList<ProjectPart::Ptr> > inputToParts;
foreach (const ProjectPart::Ptr &projectPart, projectParts) {
if (projectPart->precompiledHeaders.isEmpty())
continue;
const QString &pch = projectPart->precompiledHeaders.first(); // TODO: support more than 1 PCH file.
if (!QFile(pch).exists())
continue;
inputToParts[pch].append(projectPart);
includes[pch].unite(QSet<QString>::fromList(projectPart->includePaths)); if (hasObjCFiles(projectPart))
frameworks[pch].unite(QSet<QString>::fromList(projectPart->frameworkPaths)); objc[pch] = true;
cVersions[pch] = std::max(cVersions.value(pch, ProjectPart::C89), projectPart->cVersion); if (hasCppFiles(projectPart))
cxxVersions[pch] = std::max(cxxVersions.value(pch, ProjectPart::CXX98), projectPart->cxxVersion); cplusplus[pch] = true;
cxxExtensionsMap[pch] = cxxExtensionsMap[pch] | projectPart->cxxExtensions;
if (hasObjCFiles(projectPart)) QSet<QByteArray> projectDefines = QSet<QByteArray>::fromList(projectPart->toolchainDefines.split('\n'));
objc[pch] = true; QMutableSetIterator<QByteArray> iter(projectDefines);
if (hasCppFiles(projectPart)) while (iter.hasNext()){
cplusplus[pch] = true; QByteArray v = iter.next();
if (v.startsWith("#define _") || v.isEmpty()) // TODO: see ProjectPart::createClangOptions
iter.remove();
}
projectDefines.unite(QSet<QByteArray>::fromList(projectPart->projectDefines.split('\n')));
QSet<QByteArray> projectDefines = QSet<QByteArray>::fromList(projectPart->toolchainDefines.split('\n')); if (definesPerPCH.contains(pch)) {
QMutableSetIterator<QByteArray> iter(projectDefines); definesPerPCH[pch].intersect(projectDefines);
while (iter.hasNext()){ } else {
QByteArray v = iter.next(); definesPerPCH[pch] = projectDefines;
if (v.startsWith("#define _") || v.isEmpty()) // TODO: see ProjectPart::createClangOptions
iter.remove();
}
projectDefines.unite(QSet<QByteArray>::fromList(projectPart->projectDefines.split('\n')));
if (definesPerPCH.contains(pch)) {
definesPerPCH[pch].intersect(projectDefines);
} else {
definesPerPCH[pch] = projectDefines;
}
qtVersions[pch] = projectPart->qtVersion;
} }
future.setProgressRange(0, definesPerPCH.size() + 1); qtVersions[pch] = projectPart->qtVersion;
future.setProgressValue(0); }
foreach (const QString &pch, inputToParts.keys()) { future.setProgressRange(0, definesPerPCH.size() + 1);
if (future.isCanceled()) future.setProgressValue(0);
return;
ProjectPart::Ptr projectPart(new ProjectPart);
projectPart->qtVersion = qtVersions[pch];
projectPart->cVersion = cVersions[pch];
projectPart->cxxVersion = cxxVersions[pch];
projectPart->cxxExtensions = cxxExtensionsMap[pch];
projectPart->includePaths = includes[pch].toList();
projectPart->frameworkPaths = frameworks[pch].toList();
QList<QByteArray> defines = definesPerPCH[pch].toList(); foreach (const QString &pch, inputToParts.keys()) {
if (!defines.isEmpty()) {
projectPart->projectDefines = defines[0];
for (int i = 1; i < defines.size(); ++i) {
projectPart->projectDefines += '\n';
projectPart->projectDefines += defines[i];
}
}
CppTools::ProjectFile::Kind prefixFileKind =
getPrefixFileKind(objc.value(pch, false), cplusplus.value(pch, false));
QStringList options = Utils::createClangOptions(projectPart, prefixFileKind);
projectPart.reset();
PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(pch, options, true);
QPair<bool, QStringList> msgs = qMakePair(true, QStringList());
if (pchInfo.isNull()) {
pchInfo = PchInfo::createWithFileName(pch, options, objc[pch]);
msgs = precompile(pchInfo);
}
pchManager->setPCHInfo(inputToParts[pch], pchInfo, msgs);
future.setProgressValue(future.progressValue() + 1);
}
} else if (pchUsage == ClangProjectSettings::PchUse_BuildSystem_Exact) {
future.setProgressRange(0, projectParts.size() + 1);
future.setProgressValue(0);
Core::MessageManager::write(
QLatin1String("updatePchInfo: switching to build system (exact)"),
Core::MessageManager::Silent);
foreach (const ProjectPart::Ptr &projectPart, projectParts) {
if (future.isCanceled())
return;
if (projectPart->precompiledHeaders.isEmpty())
continue;
const QString &pch = projectPart->precompiledHeaders.first(); // TODO: support more than 1 PCH file.
if (!QFile(pch).exists())
continue;
const bool hasObjC = hasObjCFiles(projectPart);
QStringList options = Utils::createClangOptions(
projectPart, getPrefixFileKind(hasObjC, hasCppFiles(projectPart)));
PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(pch, options, false);
QPair<bool, QStringList> msgs = qMakePair(true, QStringList());
if (pchInfo.isNull()) {
pchInfo = PchInfo::createWithFileName(pch, options, hasObjC);
msgs = precompile(pchInfo);
}
pchManager->setPCHInfo(QList<ProjectPart::Ptr>() << projectPart,
pchInfo, msgs);
future.setProgressValue(future.progressValue() + 1);
}
} else if (pchUsage == ClangProjectSettings::PchUse_Custom) {
future.setProgressRange(0, 2);
future.setProgressValue(0);
Core::MessageManager::write(
QLatin1String("updatePchInfo: switching to custom") + customPchFile,
Core::MessageManager::Silent);
QSet<QString> includes, frameworks;
bool objc = false;
bool cplusplus = false;
ProjectPart::Ptr united(new ProjectPart());
united->cxxVersion = ProjectPart::CXX98;
foreach (const ProjectPart::Ptr &projectPart, projectParts) {
includes.unite(QSet<QString>::fromList(projectPart->includePaths));
frameworks.unite(QSet<QString>::fromList(projectPart->frameworkPaths));
united->cVersion = std::max(united->cVersion, projectPart->cVersion);
united->cxxVersion = std::max(united->cxxVersion, projectPart->cxxVersion);
united->qtVersion = std::max(united->qtVersion, projectPart->qtVersion);
objc |= hasObjCFiles(projectPart);
cplusplus |= hasCppFiles(projectPart);
}
united->frameworkPaths = frameworks.toList();
united->includePaths = includes.toList();
QStringList opts = Utils::createClangOptions(
united, getPrefixFileKind(objc, cplusplus));
united.clear();
PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(customPchFile, opts, true);
QPair<bool, QStringList> msgs = qMakePair(true, QStringList());;
if (future.isCanceled()) if (future.isCanceled())
return; return;
ProjectPart::Ptr projectPart(new ProjectPart);
projectPart->qtVersion = qtVersions[pch];
projectPart->cVersion = cVersions[pch];
projectPart->cxxVersion = cxxVersions[pch];
projectPart->cxxExtensions = cxxExtensionsMap[pch];
projectPart->includePaths = includes[pch].toList();
projectPart->frameworkPaths = frameworks[pch].toList();
QList<QByteArray> defines = definesPerPCH[pch].toList();
if (!defines.isEmpty()) {
projectPart->projectDefines = defines[0];
for (int i = 1; i < defines.size(); ++i) {
projectPart->projectDefines += '\n';
projectPart->projectDefines += defines[i];
}
}
CppTools::ProjectFile::Kind prefixFileKind =
getPrefixFileKind(objc.value(pch, false), cplusplus.value(pch, false));
QStringList options = Utils::createClangOptions(projectPart, prefixFileKind);
projectPart.reset();
PchManager *pchManager = PchManager::instance();
PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(pch, options, true);
QPair<bool, QStringList> msgs = qMakePair(true, QStringList());
if (pchInfo.isNull()) { if (pchInfo.isNull()) {
pchInfo = PchInfo::createWithFileName(customPchFile, opts, objc);
pchInfo = PchInfo::createWithFileName(pch, options, objc[pch]);
msgs = precompile(pchInfo); msgs = precompile(pchInfo);
} }
pchManager->setPCHInfo(projectParts, pchInfo, msgs); pchManager->setPCHInfo(inputToParts[pch], pchInfo, msgs);
future.setProgressValue(1); future.setProgressValue(future.progressValue() + 1);
} }
future.setProgressValue(future.progressValue() + 1); future.setProgressValue(future.progressValue() + 1);
} }
void PchManager::doPchInfoUpdateExact(QFutureInterface<void> &future, const PchManager::UpdateParams &params)
{
future.setProgressRange(0, params.projectParts.size() + 1);
future.setProgressValue(0);
foreach (const ProjectPart::Ptr &projectPart, params.projectParts) {
if (future.isCanceled())
return;
if (projectPart->precompiledHeaders.isEmpty())
continue;
const QString &pch = projectPart->precompiledHeaders.first(); // TODO: support more than 1 PCH file.
if (!QFile(pch).exists())
continue;
const bool hasObjC = hasObjCFiles(projectPart);
QStringList options = Utils::createClangOptions(
projectPart, getPrefixFileKind(hasObjC, hasCppFiles(projectPart)));
PchManager *pchManager = PchManager::instance();
PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(pch, options, false);
QPair<bool, QStringList> msgs = qMakePair(true, QStringList());
if (pchInfo.isNull()) {
pchInfo = PchInfo::createWithFileName(pch, options, hasObjC);
msgs = precompile(pchInfo);
}
pchManager->setPCHInfo(QList<ProjectPart::Ptr>() << projectPart,
pchInfo, msgs);
future.setProgressValue(future.progressValue() + 1);
}
future.setProgressValue(future.progressValue() + 1);
}
void PchManager::doPchInfoUpdateCustom(QFutureInterface<void> &future, const PchManager::UpdateParams &params)
{
future.setProgressRange(0, 1);
future.setProgressValue(0);
QSet<QString> includes, frameworks;
bool objc = false;
bool cplusplus = false;
ProjectPart::Ptr united(new ProjectPart());
united->cxxVersion = ProjectPart::CXX98;
foreach (const ProjectPart::Ptr &projectPart, params.projectParts) {
includes.unite(QSet<QString>::fromList(projectPart->includePaths));
frameworks.unite(QSet<QString>::fromList(projectPart->frameworkPaths));
united->cVersion = std::max(united->cVersion, projectPart->cVersion);
united->cxxVersion = std::max(united->cxxVersion, projectPart->cxxVersion);
united->qtVersion = std::max(united->qtVersion, projectPart->qtVersion);
objc |= hasObjCFiles(projectPart);
cplusplus |= hasCppFiles(projectPart);
}
united->frameworkPaths = frameworks.toList();
united->includePaths = includes.toList();
QStringList opts = Utils::createClangOptions(
united, getPrefixFileKind(objc, cplusplus));
united.clear();
PchManager *pchManager = PchManager::instance();
PchInfo::Ptr pchInfo = pchManager->findMatchingPCH(params.customPchFile, opts, true);
QPair<bool, QStringList> msgs = qMakePair(true, QStringList());;
if (future.isCanceled())
return;
if (pchInfo.isNull()) {
pchInfo = PchInfo::createWithFileName(params.customPchFile, opts, objc);
msgs = precompile(pchInfo);
}
pchManager->setPCHInfo(params.projectParts, pchInfo, msgs);
future.setProgressValue(1);
}
PchInfo::Ptr PchManager::findMatchingPCH(const QString &inputFileName, PchInfo::Ptr PchManager::findMatchingPCH(const QString &inputFileName,
const QStringList &options, const QStringList &options,
bool fuzzyMatching) const bool fuzzyMatching) const

View File

@@ -75,12 +75,21 @@ private slots:
void updateActivePchFiles(); void updateActivePchFiles();
private: private:
struct UpdateParams {
UpdateParams(const QString &customPchFile, const QList<ProjectPart::Ptr> &projectParts)
: customPchFile(customPchFile) , projectParts(projectParts) {}
const QString customPchFile;
const QList<ProjectPart::Ptr> projectParts;
};
void updatePchInfo(ClangProjectSettings *cps, void updatePchInfo(ClangProjectSettings *cps,
const QList<ProjectPart::Ptr> &projectParts); const QList<ProjectPart::Ptr> &projectParts);
static void doPchInfoUpdate(QFutureInterface<void> &future,
ClangProjectSettings::PchUsage pchUsage, static void doPchInfoUpdateNone(QFutureInterface<void> &future, const UpdateParams &params);
const QString customPchFile, static void doPchInfoUpdateFuzzy(QFutureInterface<void> &future, const UpdateParams &params);
const QList<ProjectPart::Ptr> projectParts); static void doPchInfoUpdateExact(QFutureInterface<void> &future, const UpdateParams &params);
static void doPchInfoUpdateCustom(QFutureInterface<void> &future, const UpdateParams &params);
void setPCHInfo(const QList<ProjectPart::Ptr> &projectParts, void setPCHInfo(const QList<ProjectPart::Ptr> &projectParts,
const PchInfo::Ptr &pchInfo, const PchInfo::Ptr &pchInfo,
const QPair<bool, QStringList> &msgs); const QPair<bool, QStringList> &msgs);