Commit bc3d4d86 authored by Tobias Hunger's avatar Tobias Hunger

GccToolChain: Improve retrieval/caching of compiler data

* Use one template to implement both Caches
* Use shared_ptr to the caches. This is necessary to not crash
  when the toolchains get deleted while the compiler is being run
* Pass language to header path retrieval code and use that information
* Add unit tests for cache used by the toolchain

Change-Id: Ic31e7c4c1ed8158af7f2cdfda8104255efb06aea
Reviewed-by: Tobias Hunger's avatarTobias Hunger <tobias.hunger@qt.io>
Reviewed-by: Nikolai Kosjar's avatarNikolai Kosjar <nikolai.kosjar@qt.io>
parent 50047466
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Copyright (C) 2017 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
......@@ -32,10 +32,13 @@
#include "headerpath.h"
#include <utils/fileutils.h>
#include <utils/optional.h>
#include <QMutex>
#include <QStringList>
#include <functional>
#include <memory>
namespace ProjectExplorer {
......@@ -51,42 +54,71 @@ class LinuxIccToolChainFactory;
// GccToolChain
// --------------------------------------------------------------------------
class HeaderPathsCache
template<class T, int Size = 16>
class Cache
{
public:
HeaderPathsCache() : m_mutex(QMutex::Recursive) {}
HeaderPathsCache(const HeaderPathsCache &other);
void insert(const QStringList &compilerCommand, const QList<HeaderPath> &headerPaths);
QList<HeaderPath> check(const QStringList &compilerCommand, bool *cacheHit) const;
Cache() { m_cache.reserve(Size); }
Cache(const Cache &other) = delete;
Cache &operator =(const Cache &other) = delete;
protected:
using CacheItem = QPair<QStringList, QList<HeaderPath>>;
using Cache = QList<CacheItem>;
Cache cache() const;
Cache(Cache &&other)
{
using std::swap;
private:
mutable QMutex m_mutex;
mutable Cache m_cache;
};
QMutexLocker otherLocker(&other.m_mutex);
swap(m_cache, other.m_cache);
}
class MacroCache
{
public:
MacroCache();
MacroCache(const MacroCache &other);
void insert(const QStringList &compilerCommand, const Macros &macros);
Macros check(const QStringList &compilerCommand) const;
Cache &operator =(Cache &&other)
{
using std::swap;
protected:
using CacheItem = QPair<QStringList, Macros>;
using Cache = QVector<CacheItem>;
Cache cache() const;
QMutexLocker locker(&m_mutex);
QMutexLocker otherLocker(&other.m_mutex);
auto temporay(std::move(other.m_cache)); // Make sure other.m_cache is empty!
swap(m_cache, temporay);
return *this;
}
void insert(const QStringList &compilerArguments, const T &values)
{
CacheItem runResults;
runResults.first = compilerArguments;
runResults.second = values;
QMutexLocker locker(&m_mutex);
if (!checkImpl(compilerArguments)) {
if (m_cache.size() < Size) {
m_cache.push_back(runResults);
} else {
std::rotate(m_cache.begin(), std::next(m_cache.begin()), m_cache.end());
m_cache.back() = runResults;
}
}
}
Utils::optional<T> check(const QStringList &compilerArguments)
{
QMutexLocker locker(&m_mutex);
return checkImpl(compilerArguments);
}
private:
// Does not lock!
Macros unlockedCheck(const QStringList &compilerCommand) const;
mutable QMutex m_mutex;
mutable Cache m_cache;
Utils::optional<T> checkImpl(const QStringList &compilerArguments)
{
auto it = std::stable_partition(m_cache.begin(), m_cache.end(), [&](const CacheItem &ci) {
return ci.first != compilerArguments;
});
if (it != m_cache.end())
return m_cache.back().second;
return {};
}
using CacheItem = QPair<QStringList, T>;
QMutex m_mutex;
QVector<CacheItem> m_cache;
};
class PROJECTEXPLORER_EXPORT GccToolChain : public ToolChain
......@@ -109,7 +141,7 @@ public:
Macros predefinedMacros(const QStringList &cxxflags) const override;
SystemHeaderPathsRunner createSystemHeaderPathsRunner() const override;
QList<HeaderPath> systemHeaderPaths(const QStringList &cxxflags,
QList<HeaderPath> systemHeaderPaths(const QStringList &flags,
const Utils::FileName &sysRoot) const override;
void addToEnvironment(Utils::Environment &env) const override;
......@@ -203,8 +235,8 @@ private:
mutable QList<HeaderPath> m_headerPaths;
mutable QString m_version;
mutable MacroCache m_predefinedMacrosCache;
mutable HeaderPathsCache m_headerPathsCache;
mutable std::shared_ptr<Cache<QVector<Macro>>> m_predefinedMacrosCache;
mutable std::shared_ptr<Cache<QList<HeaderPath>>> m_headerPathsCache;
friend class Internal::GccToolChainConfigWidget;
friend class Internal::GccToolChainFactory;
......
......@@ -128,7 +128,8 @@ public:
virtual Macros predefinedMacros(const QStringList &cxxflags) const = 0;
// A SystemHeaderPathsRunner is created in the ui thread and runs in another thread.
using SystemHeaderPathsRunner = std::function<QList<HeaderPath>(const QStringList &cxxflags, const QString &sysRoot)>;
using SystemHeaderPathsRunner = std::function<QList<HeaderPath>(const QStringList &cxxflags,
const QString &sysRoot)>;
virtual SystemHeaderPathsRunner createSystemHeaderPathsRunner() const = 0;
virtual QList<HeaderPath> systemHeaderPaths(const QStringList &cxxflags,
const Utils::FileName &sysRoot) const = 0;
......
......@@ -14,6 +14,7 @@ SUBDIRS += \
generichighlighter \
profilewriter \
treeviewfind \
toolchaincache \
qtcprocess \
json \
utils \
......
......@@ -25,6 +25,7 @@ Project {
"sdktool/sdktool.qbs",
"timeline/timeline.qbs",
"treeviewfind/treeviewfind.qbs",
"toolchaincache/toolchaincache.qbs",
"utils/utils.qbs",
"valgrind/valgrind.qbs"
].concat(project.additionalAutotests)
......
QT -= gui
include(../qttest.pri)
SOURCES += \
tst_toolchaincache.cpp
import qbs
QtcAutotest {
name: "ToolChainCache autotest"
Depends { name: "ProjectExplorer" }
Group {
name: "Test sources"
files: "tst_toolchaincache.cpp"
}
}
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include <QtTest>
#include <projectexplorer/gcctoolchain.h>
//////////////// the actual autotest
class tst_ToolChainCache : public QObject
{
Q_OBJECT
private slots:
void insertOne();
void insertOneOne();
void insertOneTwo();
void insertOneTwoThree();
void insertOneTwoOneThree();
};
void tst_ToolChainCache::insertOne()
{
const QStringList key1 = {"one"};
const QString value1 = "value1";
ProjectExplorer::Cache<QString, 2> cache;
cache.insert(key1, value1);
QVERIFY(bool(cache.check(key1)));
QCOMPARE(cache.check(key1).value(), value1);
QVERIFY(!cache.check({"other"}));
}
void tst_ToolChainCache::insertOneOne()
{
const QStringList key1 = {"one"};
const QString value1 = "value1";
ProjectExplorer::Cache<QString, 2> cache;
cache.insert(key1, value1);
cache.insert(key1, value1);
QVERIFY(bool(cache.check(key1)));
QCOMPARE(cache.check(key1).value(), value1);
QVERIFY(!cache.check({"other"}));
}
void tst_ToolChainCache::insertOneTwo()
{
const QStringList key1 = {"one"};
const QString value1 = "value1";
const QStringList key2 = {"two"};
const QString value2 = "value2";
ProjectExplorer::Cache<QString, 2> cache;
cache.insert(key1, value1);
cache.insert(key2, value2);
QVERIFY(bool(cache.check(key1)));
QCOMPARE(cache.check(key1).value(), value1);
QVERIFY(bool(cache.check(key2)));
QCOMPARE(cache.check(key2).value(), value2);
QVERIFY(!cache.check({"other"}));
}
void tst_ToolChainCache::insertOneTwoThree()
{
const QStringList key1 = {"one"};
const QString value1 = "value1";
const QStringList key2 = {"two"};
const QString value2 = "value2";
const QStringList key3 = {"three"};
const QString value3 = "value3";
ProjectExplorer::Cache<QString, 2> cache;
cache.insert(key1, value1);
cache.insert(key2, value2);
cache.insert(key3, value3);
QVERIFY(!cache.check(key1)); // key1 was evicted
QVERIFY(bool(cache.check(key2)));
QCOMPARE(cache.check(key2).value(), value2);
QVERIFY(bool(cache.check(key3)));
QCOMPARE(cache.check(key3).value(), value3);
QVERIFY(!cache.check({"other"}));
}
void tst_ToolChainCache::insertOneTwoOneThree()
{
const QStringList key1 = {"one"};
const QString value1 = "value1";
const QStringList key2 = {"two"};
const QString value2 = "value2";
const QStringList key3 = {"three"};
const QString value3 = "value3";
ProjectExplorer::Cache<QString, 2> cache;
cache.insert(key1, value1);
cache.insert(key2, value2);
cache.insert(key1, value1);
cache.insert(key3, value3);
QVERIFY(bool(cache.check(key1)));
QCOMPARE(cache.check(key1).value(), value1);
QVERIFY(!cache.check(key2)); // key2 was evicted
QVERIFY(bool(cache.check(key3)));
QCOMPARE(cache.check(key3).value(), value3);
QVERIFY(!cache.check({"other"}));
}
QTEST_MAIN(tst_ToolChainCache)
#include "tst_toolchaincache.moc"
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment