Refactor HighlightingResultReporter

In some unlikely circumstances it may happen that
HighlightingResultReporter might be destroyed before
returning from the HighlightingResultReporter::start()
function. This may lead to undefined behavior.

Refactor the HighlightingResultReporter so that instead
of using QRunnable subclass we define simple function
returning the QFuture object directly.

Change-Id: Ib833771a7e46e87c83d10b59ca056a0147fabe88
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Jarek Kobus
2021-10-20 13:27:01 +02:00
parent ec633061d4
commit 0101808407
4 changed files with 64 additions and 130 deletions

View File

@@ -37,6 +37,8 @@
#include <diagnosticcontainer.h> #include <diagnosticcontainer.h>
#include <sourcelocationcontainer.h> #include <sourcelocationcontainer.h>
#include <clangsupport/tokeninfocontainer.h>
#include <cppeditor/builtincursorinfo.h> #include <cppeditor/builtincursorinfo.h>
#include <cppeditor/clangdiagnosticconfigsmodel.h> #include <cppeditor/clangdiagnosticconfigsmodel.h>
#include <cppeditor/compileroptionsbuilder.h> #include <cppeditor/compileroptionsbuilder.h>
@@ -271,10 +273,7 @@ void ClangEditorDocumentProcessor::updateHighlighting(
emit ifdefedOutBlocksUpdated(documentRevision, skippedPreprocessorBlocks); emit ifdefedOutBlocksUpdated(documentRevision, skippedPreprocessorBlocks);
m_semanticHighlighter.setHighlightingRunner( m_semanticHighlighter.setHighlightingRunner(
[tokenInfos]() { [tokenInfos]() { return highlightResults(tokenInfos); });
auto *reporter = new HighlightingResultReporter(tokenInfos);
return reporter->start();
});
m_semanticHighlighter.run(); m_semanticHighlighter.run();
} }
} }

View File

@@ -25,12 +25,13 @@
#include "clanghighlightingresultreporter.h" #include "clanghighlightingresultreporter.h"
#include <clangsupport/tokeninfocontainer.h>
#include <cppeditor/semantichighlighter.h> #include <cppeditor/semantichighlighter.h>
#include <texteditor/semantichighlighter.h>
#include <texteditor/textstyles.h> #include <texteditor/textstyles.h>
#include <utils/runextensions.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <QFuture>
namespace { namespace {
TextEditor::TextStyle toTextStyle(ClangBackEnd::HighlightingType type) TextEditor::TextStyle toTextStyle(ClangBackEnd::HighlightingType type)
@@ -161,78 +162,62 @@ TextEditor::HighlightingResult toHighlightingResult(
return result; return result;
} }
} // anonymous void highlightResultsImpl(QFutureInterface<TextEditor::HighlightingResult> &fi,
const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos,
namespace ClangCodeModel { int chunkSize)
namespace Internal {
HighlightingResultReporter::HighlightingResultReporter(
const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos)
: m_tokenInfos(tokenInfos)
{ {
m_chunksToReport.reserve(m_chunkSize + 1); if (fi.isCanceled())
}
void HighlightingResultReporter::reportChunkWise(
const TextEditor::HighlightingResult &highlightingResult)
{
if (m_chunksToReport.size() >= m_chunkSize) {
if (m_flushRequested && highlightingResult.line != m_flushLine) {
reportAndClearCurrentChunks();
} else if (!m_flushRequested) {
m_flushRequested = true;
m_flushLine = highlightingResult.line;
}
}
m_chunksToReport.append(highlightingResult);
}
void HighlightingResultReporter::reportAndClearCurrentChunks()
{
m_flushRequested = false;
m_flushLine = 0;
if (!m_chunksToReport.isEmpty()) {
reportResults(m_chunksToReport);
m_chunksToReport.erase(m_chunksToReport.begin(), m_chunksToReport.end());
}
}
void HighlightingResultReporter::setChunkSize(int chunkSize)
{
m_chunkSize = chunkSize;
}
void HighlightingResultReporter::run()
{
run_internal();
reportFinished();
}
void HighlightingResultReporter::run_internal()
{
if (isCanceled())
return; return;
using ClangBackEnd::HighlightingType; QVector<TextEditor::HighlightingResult> chunksToReport;
chunksToReport.reserve(chunkSize + 1);
for (const auto &tokenInfo : qAsConst(m_tokenInfos)) using ClangBackEnd::HighlightingType;
bool flushRequested = false;
int flushLine = 0;
auto reportAndClearCurrentChunks = [&] {
flushRequested = false;
flushLine = 0;
if (!chunksToReport.isEmpty()) {
fi.reportResults(chunksToReport);
chunksToReport.erase(chunksToReport.begin(), chunksToReport.end());
}
};
auto reportChunkWise = [&](const TextEditor::HighlightingResult &highlightingResult) {
if (chunksToReport.size() >= chunkSize) {
if (flushRequested && highlightingResult.line != flushLine) {
reportAndClearCurrentChunks();
} else if (!flushRequested) {
flushRequested = true;
flushLine = highlightingResult.line;
}
}
chunksToReport.append(highlightingResult);
};
for (const auto &tokenInfo : tokenInfos)
reportChunkWise(toHighlightingResult(tokenInfo)); reportChunkWise(toHighlightingResult(tokenInfo));
if (isCanceled()) if (fi.isCanceled())
return; return;
reportAndClearCurrentChunks(); reportAndClearCurrentChunks();
} }
QFuture<TextEditor::HighlightingResult> HighlightingResultReporter::start() } // anonymous
namespace ClangCodeModel {
namespace Internal {
QFuture<TextEditor::HighlightingResult> highlightResults(
const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos,
int chunkSize)
{ {
this->setRunnable(this); return Utils::runAsync(highlightResultsImpl, tokenInfos, chunkSize);
this->reportStarted();
QFuture<TextEditor::HighlightingResult> future = this->future();
QThreadPool::globalInstance()->start(this, QThread::LowestPriority);
return future;
} }
} // namespace Internal } // namespace Internal

View File

@@ -25,48 +25,18 @@
#pragma once #pragma once
#include <QFutureInterface> #include <QFuture>
#include <QObject> #include <QVector>
#include <QRunnable>
#include <QThreadPool>
#include <texteditor/semantichighlighter.h> namespace TextEditor { class HighlightingResult; }
namespace ClangBackEnd { class TokenInfoContainer; }
#include <clangsupport/tokeninfocontainer.h>
namespace ClangCodeModel { namespace ClangCodeModel {
namespace Internal { namespace Internal {
class HighlightingResultReporter: QFuture<TextEditor::HighlightingResult> highlightResults(
public QObject, const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos,
public QRunnable, int chunkSize = 100);
public QFutureInterface<TextEditor::HighlightingResult>
{
Q_OBJECT
public:
HighlightingResultReporter(const QVector<ClangBackEnd::TokenInfoContainer> &tokenInfos);
void setChunkSize(int chunkSize);
QFuture<TextEditor::HighlightingResult> start();
private:
void run() override;
void run_internal();
void reportChunkWise(const TextEditor::HighlightingResult &highlightingResult);
void reportAndClearCurrentChunks();
private:
QVector<ClangBackEnd::TokenInfoContainer> m_tokenInfos;
QVector<TextEditor::HighlightingResult> m_chunksToReport;
int m_chunkSize = 100;
bool m_flushRequested = false;
int m_flushLine = 0;
};
} // namespace Internal } // namespace Internal
} // namespace ClangCodeModel } // namespace ClangCodeModel

View File

@@ -43,7 +43,6 @@ using ClangBackEnd::Document;
using ClangBackEnd::Documents; using ClangBackEnd::Documents;
using ClangBackEnd::UnsavedFiles; using ClangBackEnd::UnsavedFiles;
using ClangBackEnd::ChunksReportedMonitor; using ClangBackEnd::ChunksReportedMonitor;
using ClangCodeModel::Internal::HighlightingResultReporter;
namespace { namespace {
@@ -87,9 +86,7 @@ QVector<TokenInfoContainer> generateTokenInfos(uint count)
TEST_F(HighlightingResultReporter, StartAndFinish) TEST_F(HighlightingResultReporter, StartAndFinish)
{ {
auto reporter = new ::HighlightingResultReporter(noTokenInfos()); auto future = ClangCodeModel::Internal::highlightResults(noTokenInfos());
auto future = reporter->start();
future.waitForFinished(); future.waitForFinished();
ASSERT_THAT(future.isFinished(), true); ASSERT_THAT(future.isFinished(), true);
@@ -97,9 +94,7 @@ TEST_F(HighlightingResultReporter, StartAndFinish)
TEST_F(HighlightingResultReporter, ReportNothingIfNothingToReport) TEST_F(HighlightingResultReporter, ReportNothingIfNothingToReport)
{ {
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(0)); auto future = ClangCodeModel::Internal::highlightResults(generateTokenInfos(0));
auto future = reporter->start();
ChunksReportedMonitor monitor(future); ChunksReportedMonitor monitor(future);
ASSERT_THAT(monitor.resultsReadyCounter(), 0L); ASSERT_THAT(monitor.resultsReadyCounter(), 0L);
@@ -107,10 +102,7 @@ TEST_F(HighlightingResultReporter, ReportNothingIfNothingToReport)
TEST_F(HighlightingResultReporter, ReportSingleResultAsOneChunk) TEST_F(HighlightingResultReporter, ReportSingleResultAsOneChunk)
{ {
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(1)); auto future = ClangCodeModel::Internal::highlightResults(generateTokenInfos(1), 1);
reporter->setChunkSize(1);
auto future = reporter->start();
ChunksReportedMonitor monitor(future); ChunksReportedMonitor monitor(future);
ASSERT_THAT(monitor.resultsReadyCounter(), 1L); ASSERT_THAT(monitor.resultsReadyCounter(), 1L);
@@ -118,11 +110,7 @@ TEST_F(HighlightingResultReporter, ReportSingleResultAsOneChunk)
TEST_F(HighlightingResultReporter, ReportRestIfChunkSizeNotReached) TEST_F(HighlightingResultReporter, ReportRestIfChunkSizeNotReached)
{ {
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(1)); auto future = ClangCodeModel::Internal::highlightResults(generateTokenInfos(1), 100);
const int notReachedChunkSize = 100;
reporter->setChunkSize(notReachedChunkSize);
auto future = reporter->start();
ChunksReportedMonitor monitor(future); ChunksReportedMonitor monitor(future);
ASSERT_THAT(monitor.resultsReadyCounter(), 1L); ASSERT_THAT(monitor.resultsReadyCounter(), 1L);
@@ -130,10 +118,7 @@ TEST_F(HighlightingResultReporter, ReportRestIfChunkSizeNotReached)
TEST_F(HighlightingResultReporter, ReportChunksWithoutRest) TEST_F(HighlightingResultReporter, ReportChunksWithoutRest)
{ {
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(4)); auto future = ClangCodeModel::Internal::highlightResults(generateTokenInfos(4), 1);
reporter->setChunkSize(1);
auto future = reporter->start();
ChunksReportedMonitor monitor(future); ChunksReportedMonitor monitor(future);
ASSERT_THAT(monitor.resultsReadyCounter(), 2L); ASSERT_THAT(monitor.resultsReadyCounter(), 2L);
@@ -141,10 +126,7 @@ TEST_F(HighlightingResultReporter, ReportChunksWithoutRest)
TEST_F(HighlightingResultReporter, ReportSingleChunkAndRest) TEST_F(HighlightingResultReporter, ReportSingleChunkAndRest)
{ {
auto reporter = new ::HighlightingResultReporter(generateTokenInfos(5)); auto future = ClangCodeModel::Internal::highlightResults(generateTokenInfos(5), 2);
reporter->setChunkSize(2);
auto future = reporter->start();
ChunksReportedMonitor monitor(future); ChunksReportedMonitor monitor(future);
ASSERT_THAT(monitor.resultsReadyCounter(), 2L); ASSERT_THAT(monitor.resultsReadyCounter(), 2L);
@@ -159,10 +141,8 @@ TEST_F(HighlightingResultReporter, ReportCompleteLines)
TokenInfoContainer(1, 2, 1, types), TokenInfoContainer(1, 2, 1, types),
TokenInfoContainer(2, 1, 1, types), TokenInfoContainer(2, 1, 1, types),
}; };
auto reporter = new ::HighlightingResultReporter(tokenInfos);
reporter->setChunkSize(1);
auto future = reporter->start(); auto future = ClangCodeModel::Internal::highlightResults(tokenInfos, 1);
ChunksReportedMonitor monitor(future); ChunksReportedMonitor monitor(future);
ASSERT_THAT(monitor.resultsReadyCounter(), 2L); ASSERT_THAT(monitor.resultsReadyCounter(), 2L);