AutotoolsBuildSystem: Avoid using sender()

According to the old explanation inside makefileParsingFinished()
the solution of using sender() can't work reliably in this
case. In scenarion where the old, deleted m_makefileParserThread
had the same address as newly allocated one we might not detect
correctly posted finished() signal of the deleted object.
According to QObject::sender() documentation:
"The pointer returned by this function becomes invalid if the
sender is destroyed", so we can't rely on its value.

Instead, add a new done() signal to the MakefileParserThread,
which is being emitted from MakefileParserThread thread (i.e. from
the thread where MakefileParserThread object lives in). In this way
the finished() signal is still posted to the caller's thread,
but this time if the MakefileParserThread object is already
deleted, the finished() signal will be gone together with the
MakefileParserThread object itself, so we also won't receive done()
signal anymore.

Simplify the internals by using std::unique_ptr.

Change-Id: I43be209ecb71539ddefd72e50e9d60bfb43c49cb
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Jarek Kobus
2022-07-21 17:48:49 +02:00
parent 10da5083fe
commit f6b37328c7
4 changed files with 23 additions and 37 deletions

View File

@@ -55,53 +55,31 @@ AutotoolsBuildSystem::~AutotoolsBuildSystem()
{ {
delete m_cppCodeModelUpdater; delete m_cppCodeModelUpdater;
if (m_makefileParserThread) { if (m_makefileParserThread)
m_makefileParserThread->wait(); m_makefileParserThread->wait();
delete m_makefileParserThread;
m_makefileParserThread = nullptr;
}
} }
void AutotoolsBuildSystem::triggerParsing() void AutotoolsBuildSystem::triggerParsing()
{ {
if (m_makefileParserThread) { // The thread is still busy parsing a previous configuration.
// The thread is still busy parsing a previous configuration. // Wait until the thread has been finished and delete it.
// Wait until the thread has been finished and delete it. // TODO: Discuss whether blocking is acceptable.
// TODO: Discuss whether blocking is acceptable. if (m_makefileParserThread)
disconnect(m_makefileParserThread,
&QThread::finished,
this,
&AutotoolsBuildSystem::makefileParsingFinished);
m_makefileParserThread->wait(); m_makefileParserThread->wait();
delete m_makefileParserThread;
m_makefileParserThread = nullptr;
}
// Parse the makefile asynchronously in a thread // Parse the makefile asynchronously in a thread
m_makefileParserThread = new MakefileParserThread(this); m_makefileParserThread.reset(new MakefileParserThread(this));
connect(m_makefileParserThread, connect(m_makefileParserThread.get(), &MakefileParserThread::done,
&MakefileParserThread::finished, this, &AutotoolsBuildSystem::makefileParsingFinished);
this,
&AutotoolsBuildSystem::makefileParsingFinished);
m_makefileParserThread->start(); m_makefileParserThread->start();
} }
void AutotoolsBuildSystem::makefileParsingFinished() void AutotoolsBuildSystem::makefileParsingFinished()
{ {
// The finished() signal is from a previous makefile-parser-thread // The parsing has been cancelled by the user. Don't show any project data at all.
// and can be skipped. This can happen, if the thread has emitted the
// finished() signal during the execution of AutotoolsBuildSystem::loadProjectTree().
// In this case the signal is in the message queue already and deleting
// the thread of course does not remove the signal again.
if (sender() != m_makefileParserThread)
return;
if (m_makefileParserThread->isCanceled()) { if (m_makefileParserThread->isCanceled()) {
// The parsing has been cancelled by the user. Don't show any m_makefileParserThread.release()->deleteLater();
// project data at all.
m_makefileParserThread->deleteLater();
m_makefileParserThread = nullptr;
return; return;
} }
@@ -151,8 +129,7 @@ void AutotoolsBuildSystem::makefileParsingFinished()
updateCppCodeModel(); updateCppCodeModel();
m_makefileParserThread->deleteLater(); m_makefileParserThread.release()->deleteLater();
m_makefileParserThread = nullptr;
emitBuildSystemUpdated(); emitBuildSystemUpdated();
} }

View File

@@ -29,6 +29,8 @@
#include <projectexplorer/buildsystem.h> #include <projectexplorer/buildsystem.h>
#include <memory>
namespace CppEditor { class CppProjectUpdater; } namespace CppEditor { class CppProjectUpdater; }
namespace AutotoolsProjectManager { namespace AutotoolsProjectManager {
@@ -63,7 +65,7 @@ private:
QStringList m_files; QStringList m_files;
/// Responsible for parsing the makefiles asynchronously in a thread /// Responsible for parsing the makefiles asynchronously in a thread
MakefileParserThread *m_makefileParserThread = nullptr; std::unique_ptr<MakefileParserThread> m_makefileParserThread;
CppEditor::CppProjectUpdater *m_cppCodeModelUpdater = nullptr; CppEditor::CppProjectUpdater *m_cppCodeModelUpdater = nullptr;
}; };

View File

@@ -35,8 +35,8 @@ MakefileParserThread::MakefileParserThread(ProjectExplorer::BuildSystem *bs)
: m_parser(bs->projectFilePath().toString()), : m_parser(bs->projectFilePath().toString()),
m_guard(bs->guardParsingRun()) m_guard(bs->guardParsingRun())
{ {
connect(&m_parser, &MakefileParser::status, connect(&m_parser, &MakefileParser::status, this, &MakefileParserThread::status);
this, &MakefileParserThread::status); connect(this, &QThread::finished, this, &MakefileParserThread::done, Qt::QueuedConnection);
} }
QStringList MakefileParserThread::sources() const QStringList MakefileParserThread::sources() const

View File

@@ -131,6 +131,13 @@ signals:
*/ */
void status(const QString &status); void status(const QString &status);
/**
* Similar to finished, but emitted from MakefileParserThread thread, i.e. from the
* thread where the MakefileParserThread lives in, not the tread that it creates.
* This helps to avoid race condition when connecting to finished() signal.
*/
void done();
private: private:
MakefileParser m_parser; ///< Is not accessible outside the thread MakefileParser m_parser; ///< Is not accessible outside the thread