AutoTest: Avoid dead-lock of file system watcher

Calling addPath() on the watcher could dead-lock on some operating
systems as we were in an asynchronous process.
Avoid calling addPath() from inside an asynchronous process and
perform this call from synchronous context.

Change-Id: I94cd401e12ccbb3526b8cc4232a9cff7ed552bbb
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Stenger
2017-03-20 15:42:08 +01:00
parent 4bdfebb4dd
commit b86011c375
2 changed files with 29 additions and 21 deletions

View File

@@ -37,13 +37,9 @@
#include <utils/hostosinfo.h> #include <utils/hostosinfo.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QFileSystemWatcher>
namespace Autotest { namespace Autotest {
namespace Internal { namespace Internal {
static QFileSystemWatcher s_directoryWatcher;
TestTreeItem *QuickTestParseResult::createTestTreeItem() const TestTreeItem *QuickTestParseResult::createTestTreeItem() const
{ {
if (itemType == TestTreeItem::Root || itemType == TestTreeItem::TestDataTag) if (itemType == TestTreeItem::Root || itemType == TestTreeItem::TestDataTag)
@@ -126,7 +122,7 @@ static QString quickTestName(const CPlusPlus::Document::Ptr &doc)
return QString(); return QString();
} }
static QList<QmlJS::Document::Ptr> scanDirectoryForQuickTestQmlFiles(const QString &srcDir) QList<QmlJS::Document::Ptr> QuickTestParser::scanDirectoryForQuickTestQmlFiles(const QString &srcDir) const
{ {
QStringList dirs(srcDir); QStringList dirs(srcDir);
QmlJS::ModelManagerInterface *qmlJsMM = QmlJSTools::Internal::ModelManager::instance(); QmlJS::ModelManagerInterface *qmlJsMM = QmlJSTools::Internal::ModelManager::instance();
@@ -142,9 +138,9 @@ static QList<QmlJS::Document::Ptr> scanDirectoryForQuickTestQmlFiles(const QStri
while (it.hasNext()) { while (it.hasNext()) {
it.next(); it.next();
QFileInfo fi(it.fileInfo().canonicalFilePath()); QFileInfo fi(it.fileInfo().canonicalFilePath());
dirs << fi.filePath(); dirs.append(fi.filePath());
} }
s_directoryWatcher.addPaths(dirs); emit updateWatchPaths(dirs);
QList<QmlJS::Document::Ptr> foundDocs; QList<QmlJS::Document::Ptr> foundDocs;
@@ -211,9 +207,9 @@ static bool checkQmlDocumentForQuickTestCode(QFutureInterface<TestParseResultPtr
return true; return true;
} }
static bool handleQtQuickTest(QFutureInterface<TestParseResultPtr> futureInterface, bool QuickTestParser::handleQtQuickTest(QFutureInterface<TestParseResultPtr> futureInterface,
CPlusPlus::Document::Ptr document, CPlusPlus::Document::Ptr document,
const Core::Id &id) const Core::Id &id) const
{ {
const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
if (quickTestName(document).isEmpty()) if (quickTestName(document).isEmpty())
@@ -229,32 +225,35 @@ static bool handleQtQuickTest(QFutureInterface<TestParseResultPtr> futureInterfa
if (srcDir.isEmpty()) if (srcDir.isEmpty())
return false; return false;
if (futureInterface.isCanceled())
return false;
const QList<QmlJS::Document::Ptr> qmlDocs = scanDirectoryForQuickTestQmlFiles(srcDir); const QList<QmlJS::Document::Ptr> qmlDocs = scanDirectoryForQuickTestQmlFiles(srcDir);
bool result = false; bool result = false;
for (const QmlJS::Document::Ptr &qmlJSDoc : qmlDocs) for (const QmlJS::Document::Ptr &qmlJSDoc : qmlDocs) {
if (futureInterface.isCanceled())
break;
result |= checkQmlDocumentForQuickTestCode(futureInterface, qmlJSDoc, id, proFile); result |= checkQmlDocumentForQuickTestCode(futureInterface, qmlJSDoc, id, proFile);
}
return result; return result;
} }
QuickTestParser::QuickTestParser() QuickTestParser::QuickTestParser()
: CppParser() : CppParser()
{ {
QObject::connect(ProjectExplorer::SessionManager::instance(), connect(ProjectExplorer::SessionManager::instance(),
&ProjectExplorer::SessionManager::startupProjectChanged, [] { &ProjectExplorer::SessionManager::startupProjectChanged, [this] {
const QStringList &dirs = s_directoryWatcher.directories(); const QStringList &dirs = m_directoryWatcher.directories();
if (!dirs.isEmpty()) if (!dirs.isEmpty())
s_directoryWatcher.removePaths(dirs); m_directoryWatcher.removePaths(dirs);
}); });
QObject::connect(&s_directoryWatcher, &QFileSystemWatcher::directoryChanged, connect(&m_directoryWatcher, &QFileSystemWatcher::directoryChanged,
[this] { TestTreeModel::instance()->parser()->emitUpdateTestTree(this); }); [this] { TestTreeModel::instance()->parser()->emitUpdateTestTree(this); });
connect(this, &QuickTestParser::updateWatchPaths,
&m_directoryWatcher, &QFileSystemWatcher::addPaths, Qt::QueuedConnection);
} }
QuickTestParser::~QuickTestParser() QuickTestParser::~QuickTestParser()
{ {
QObject::disconnect(&s_directoryWatcher, 0, 0, 0);
const QStringList &dirs = s_directoryWatcher.directories();
if (!dirs.isEmpty())
s_directoryWatcher.removePaths(dirs);
} }
void QuickTestParser::init(const QStringList &filesToParse) void QuickTestParser::init(const QStringList &filesToParse)

View File

@@ -29,6 +29,8 @@
#include <qmljs/qmljsdocument.h> #include <qmljs/qmljsdocument.h>
#include <QFileSystemWatcher>
namespace Autotest { namespace Autotest {
namespace Internal { namespace Internal {
@@ -39,8 +41,9 @@ public:
TestTreeItem *createTestTreeItem() const override; TestTreeItem *createTestTreeItem() const override;
}; };
class QuickTestParser : public CppParser class QuickTestParser : public QObject, public CppParser
{ {
Q_OBJECT
public: public:
QuickTestParser(); QuickTestParser();
virtual ~QuickTestParser(); virtual ~QuickTestParser();
@@ -48,9 +51,15 @@ public:
void release() override; void release() override;
bool processDocument(QFutureInterface<TestParseResultPtr> futureInterface, bool processDocument(QFutureInterface<TestParseResultPtr> futureInterface,
const QString &fileName) override; const QString &fileName) override;
signals:
void updateWatchPaths(const QStringList &directories) const;
private: private:
bool handleQtQuickTest(QFutureInterface<TestParseResultPtr> futureInterface,
CPlusPlus::Document::Ptr document, const Core::Id &id) const;
QList<QmlJS::Document::Ptr> scanDirectoryForQuickTestQmlFiles(const QString &srcDir) const;
QmlJS::Snapshot m_qmlSnapshot; QmlJS::Snapshot m_qmlSnapshot;
QHash<QString, QString> m_proFilesForQmlFiles; QHash<QString, QString> m_proFilesForQmlFiles;
QFileSystemWatcher m_directoryWatcher;
}; };
} // namespace Internal } // namespace Internal