Commit c12d01fb authored by Nikolai Kosjar's avatar Nikolai Kosjar
Browse files

Clang: Add job queue for each document



...in preparation for follow-up changes. This will enable e.g. a timer
per document.

This does not change any behavior yet.

Change-Id: Ic1dc06de602373c666d47ce7a95ab99e56d389d5
Reviewed-by: David Schulz's avatarDavid Schulz <david.schulz@qt.io>
parent b64bb0a7
......@@ -64,6 +64,11 @@ public:
return future;
}
void preventFinalization() override
{
m_futureWatcher.disconnect();
}
private:
Runner m_runner;
QFutureWatcher<Result> m_futureWatcher;
......
......@@ -41,7 +41,9 @@ HEADERS += $$PWD/clangcodemodelserver.h \
$$PWD/clangtranslationunit.h \
$$PWD/clangunsavedfilesshallowarguments.h \
$$PWD/clangupdatedocumentannotationsjob.h \
$$PWD/clangexceptions.h
$$PWD/clangexceptions.h \
$$PWD/clangdocumentprocessor.h \
$$PWD/clangdocumentprocessors.h \
SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/codecompleter.cpp \
......@@ -80,4 +82,6 @@ SOURCES += $$PWD/clangcodemodelserver.cpp \
$$PWD/clangtranslationunit.cpp \
$$PWD/clangunsavedfilesshallowarguments.cpp \
$$PWD/clangupdatedocumentannotationsjob.cpp \
$$PWD/clangexceptions.cpp
$$PWD/clangexceptions.cpp \
$$PWD/clangdocumentprocessor.cpp \
$$PWD/clangdocumentprocessors.cpp \
......@@ -141,6 +141,10 @@ void ClangCodeModelServer::unregisterTranslationUnitsForEditor(const ClangBackEn
TIME_SCOPE_DURATION("ClangCodeModelServer::unregisterTranslationUnitsForEditor");
try {
for (const auto &fileContainer : message.fileContainers()) {
const Document &document = documents.document(fileContainer);
documentProcessors().remove(document);
}
documents.remove(message.fileContainers());
unsavedFiles.remove(message.fileContainers());
} catch (const std::exception &exception) {
......@@ -211,8 +215,9 @@ void ClangCodeModelServer::completeCode(const ClangBackEnd::CompleteCodeMessage
jobRequest.column = message.column();
jobRequest.ticketNumber = message.ticketNumber();
jobs().add(jobRequest);
jobs().process();
DocumentProcessor processor = documentProcessors().processor(document);
processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::completeCode:" << exception.what();
}
......@@ -229,8 +234,9 @@ void ClangCodeModelServer::requestDocumentAnnotations(const RequestDocumentAnnot
const JobRequest jobRequest = createJobRequest(document,
JobRequest::Type::RequestDocumentAnnotations);
jobs().add(jobRequest);
jobs().process();
DocumentProcessor processor = documentProcessors().processor(document);
processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::requestDocumentAnnotations:" << exception.what();
}
......@@ -260,9 +266,14 @@ void ClangCodeModelServer::startDocumentAnnotationsTimerIfFileIsNotOpenAsDocumen
updateDocumentAnnotationsTimer.start(0);
}
const Jobs &ClangCodeModelServer::jobsForTestOnly()
QList<Jobs::RunningJob> ClangCodeModelServer::runningJobsForTestsOnly()
{
return jobs();
return documentProcessors().runningJobs();
}
int ClangCodeModelServer::queueSizeForTestsOnly()
{
return documentProcessors().queueSize();
}
bool ClangCodeModelServer::isTimerRunningForTestOnly() const
......@@ -270,28 +281,26 @@ bool ClangCodeModelServer::isTimerRunningForTestOnly() const
return updateDocumentAnnotationsTimer.isActive();
}
void ClangCodeModelServer::addJobRequestsForDirtyAndVisibleDocuments()
void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments()
{
for (const auto &document : documents.documents()) {
if (document.isNeedingReparse() && document.isVisibleInEditor())
jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations));
if (document.isNeedingReparse() && document.isVisibleInEditor()) {
DocumentProcessor processor = documentProcessors().processor(document);
processor.addJob(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations));
}
}
}
void ClangCodeModelServer::processJobsForDirtyAndVisibleDocuments()
{
addJobRequestsForDirtyAndVisibleDocuments();
jobs().process();
documentProcessors().process();
}
void ClangCodeModelServer::processInitialJobsForDocuments(const std::vector<Document> &documents)
{
for (const auto &document : documents) {
jobs().add(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations));
jobs().add(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble));
DocumentProcessor processor = documentProcessors().create(document);
processor.addJob(createJobRequest(document, JobRequest::Type::UpdateDocumentAnnotations));
processor.addJob(createJobRequest(document, JobRequest::Type::CreateInitialDocumentPreamble));
processor.process();
}
jobs().process();
}
JobRequest ClangCodeModelServer::createJobRequest(const Document &document,
......@@ -315,16 +324,16 @@ void ClangCodeModelServer::setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(i
updateDocumentAnnotationsTimeOutInMs = value;
}
Jobs &ClangCodeModelServer::jobs()
DocumentProcessors &ClangCodeModelServer::documentProcessors()
{
if (!jobs_) {
// Jobs needs a reference to the client, but the client is not known at
// construction time of ClangCodeModelServer, so construct Jobs in a
// lazy manner.
jobs_.reset(new Jobs(documents, unsavedFiles, projects, *client()));
if (!documentProcessors_) {
// DocumentProcessors needs a reference to the client, but the client
// is not known at construction time of ClangCodeModelServer, so
// construct DocumentProcessors in a lazy manner.
documentProcessors_.reset(new DocumentProcessors(documents, unsavedFiles, projects, *client()));
}
return *jobs_.data();
return *documentProcessors_.data();
}
} // namespace ClangBackEnd
......@@ -31,8 +31,9 @@
#include "projects.h"
#include "clangdocument.h"
#include "clangdocuments.h"
#include "clangdocumentprocessors.h"
#include "clangjobrequest.h"
#include "unsavedfiles.h"
#include "clangjobs.h"
#include <utf8string.h>
......@@ -58,14 +59,15 @@ public:
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
public /*for tests*/:
public: // for tests
const Documents &documentsForTestOnly() const;
const Jobs &jobsForTestOnly();
QList<Jobs::RunningJob> runningJobsForTestsOnly();
int queueSizeForTestsOnly();
bool isTimerRunningForTestOnly() const;
void setUpdateDocumentAnnotationsTimeOutInMsForTestsOnly(int value);
private:
Jobs &jobs();
DocumentProcessors &documentProcessors();
void startDocumentAnnotationsTimerIfFileIsNotOpenAsDocument(const Utf8String &filePath);
void addJobRequestsForDirtyAndVisibleDocuments();
......@@ -78,7 +80,8 @@ private:
ProjectParts projects;
UnsavedFiles unsavedFiles;
Documents documents;
QScopedPointer<Jobs> jobs_;
QScopedPointer<DocumentProcessors> documentProcessors_; // Delayed initialization
QTimer updateDocumentAnnotationsTimer;
int updateDocumentAnnotationsTimeOutInMs;
......
/****************************************************************************
**
** 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 "clangdocumentprocessor.h"
#include "clangjobs.h"
#include "clangdocument.h"
namespace ClangBackEnd {
class DocumentProcessorData
{
public:
DocumentProcessorData(const Document &document,
Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client)
: document(document)
, jobs(documents, unsavedFiles, projects, client)
{}
public:
Document document;
Jobs jobs;
};
DocumentProcessor::DocumentProcessor(const Document &document,
Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client)
: d(std::make_shared<DocumentProcessorData>(document,
documents,
unsavedFiles,
projects,
client))
{
}
void DocumentProcessor::addJob(const JobRequest &jobRequest)
{
d->jobs.add(jobRequest);
}
JobRequests DocumentProcessor::process()
{
return d->jobs.process();
}
Document DocumentProcessor::document() const
{
return d->document;
}
QList<Jobs::RunningJob> DocumentProcessor::runningJobs() const
{
return d->jobs.runningJobs();
}
int DocumentProcessor::queueSize() const
{
return d->jobs.queue().size();
}
} // namespace ClangBackEnd
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "clangjobrequest.h"
#include "clangjobs.h"
#include <memory>
namespace ClangBackEnd {
class ClangCodeModelClientInterface;
class Document;
class Documents;
class DocumentProcessorData;
class JobRequest;
class ProjectParts;
class UnsavedFiles;
class DocumentProcessor
{
public:
DocumentProcessor(const Document &document,
Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client);
void addJob(const JobRequest &jobRequest);
JobRequests process();
Document document() const;
public: // for tests
QList<Jobs::RunningJob> runningJobs() const;
int queueSize() const;
private:
std::shared_ptr<DocumentProcessorData> d;
};
} // namespace ClangBackEnd
/****************************************************************************
**
** 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 "clangdocumentprocessors.h"
#include "clangdocument.h"
#include "clangexceptions.h"
namespace ClangBackEnd {
DocumentProcessors::DocumentProcessors(Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client)
: m_documents(documents)
, m_unsavedFiles(unsavedFiles)
, m_projects(projects)
, m_client(client)
{
}
static bool operator<(const DocumentId &lhs, const DocumentId &rhs)
{
return lhs.filePath < rhs.filePath
|| (lhs.filePath == rhs.filePath && lhs.projectPartId < lhs.projectPartId);
}
DocumentProcessor DocumentProcessors::create(const Document &document)
{
const DocumentId id{document.filePath(), document.projectPartId()};
if (m_processors.contains(id))
throw DocumentProcessorAlreadyExists(document.filePath(), document.projectPartId());
const DocumentProcessor element(document, m_documents, m_unsavedFiles, m_projects, m_client);
m_processors.insert(id, element);
return element;
}
DocumentProcessor DocumentProcessors::processor(const Document &document)
{
const DocumentId id{document.filePath(), document.projectPartId()};
const auto it = m_processors.find(id);
if (it == m_processors.end())
throw DocumentProcessorDoesNotExist(document.filePath(), document.projectPartId());
return *it;
}
QList<DocumentProcessor> DocumentProcessors::processors() const
{
return m_processors.values();
}
void DocumentProcessors::remove(const Document &document)
{
const DocumentId id{document.filePath(), document.projectPartId()};
const int itemsRemoved = m_processors.remove(id);
if (itemsRemoved != 1)
throw DocumentProcessorDoesNotExist(document.filePath(), document.projectPartId());
}
JobRequests DocumentProcessors::process()
{
JobRequests jobsStarted;
for (auto &processor : m_processors)
jobsStarted += processor.process();
return jobsStarted;
}
QList<Jobs::RunningJob> DocumentProcessors::runningJobs() const
{
QList<Jobs::RunningJob> jobs;
for (auto &processor : m_processors)
jobs += processor.runningJobs();
return jobs;
}
int DocumentProcessors::queueSize() const
{
int total = 0;
for (auto &processor : m_processors)
total += processor.queueSize();
return total;
}
} // namespace ClangBackEnd
/****************************************************************************
**
** 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.
**
****************************************************************************/
#pragma once
#include "clangdocumentprocessor.h"
#include "clangjobs.h"
#include <utf8string.h>
#include <QMap>
namespace ClangBackEnd {
class Document;
class DocumentProcessor;
class DocumentId {
public:
Utf8String filePath;
Utf8String projectPartId;
};
class DocumentProcessors
{
public:
DocumentProcessors(Documents &documents,
UnsavedFiles &unsavedFiles,
ProjectParts &projects,
ClangCodeModelClientInterface &client);
DocumentProcessor create(const Document &document);
DocumentProcessor processor(const Document &document);
void remove(const Document &document);
JobRequests process();
public: // for tests
QList<DocumentProcessor> processors() const;
QList<Jobs::RunningJob> runningJobs() const;
int queueSize() const;
private:
Documents &m_documents;
UnsavedFiles &m_unsavedFiles;
ProjectParts &m_projects;
ClangCodeModelClientInterface &m_client;
QMap<DocumentId, DocumentProcessor> m_processors;
};
} // namespace ClangBackEnd
......@@ -74,4 +74,24 @@ DocumentIsNullException::DocumentIsNullException()
m_info = Utf8String::fromUtf8("Tried to access a null Document!");
}
DocumentProcessorAlreadyExists::DocumentProcessorAlreadyExists(const Utf8String &filePath,
const Utf8String &projectPartId)
{
m_info = Utf8StringLiteral("Document processor for file '")
+ filePath
+ Utf8StringLiteral("' and project part id '")
+ projectPartId
+ Utf8StringLiteral("' already exists!");
}
DocumentProcessorDoesNotExist::DocumentProcessorDoesNotExist(const Utf8String &filePath,
const Utf8String &projectPartId)
{
m_info = Utf8StringLiteral("Document processor for file '")
+ filePath
+ Utf8StringLiteral("' and project part id '")
+ projectPartId
+ Utf8StringLiteral("' does not exist!");
}
} // namespace ClangBackEnd
......@@ -74,4 +74,18 @@ public:
DocumentIsNullException();
};
class DocumentProcessorAlreadyExists : public ClangBaseException
{
public:
DocumentProcessorAlreadyExists(const Utf8String &filePath,
const Utf8String &projectPartId);
};
class DocumentProcessorDoesNotExist : public ClangBaseException
{
public:
DocumentProcessorDoesNotExist(const Utf8String &filePath,
const Utf8String &projectPartId);
};
} // namespace ClangBackEnd
......@@ -56,6 +56,8 @@ public:
virtual QFuture<void> runAsync() = 0;
virtual void finalizeAsyncRun() = 0;
virtual void preventFinalization() = 0;
public: // for tests
bool isFinished() const;
void setIsFinished(bool isFinished);
......
......@@ -53,9 +53,15 @@ Jobs::Jobs(Documents &documents,
Jobs::~Jobs()
{
foreach (IAsyncJob *asyncJob, m_running.keys())
asyncJob->preventFinalization();
QFutureSynchronizer<void> waitForFinishedJobs;
foreach