diff options
Diffstat (limited to 'src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp')
-rw-r--r-- | src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp | 133 |
1 files changed, 115 insertions, 18 deletions
diff --git a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp index b92a35f854..6b628cdb13 100644 --- a/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp +++ b/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp @@ -48,6 +48,37 @@ static void readPatch(QFutureInterface<QList<FileData>> &futureInterface, futureInterface.reportResult(fileDataList); } +///////////////////// + +// We need a way to disconnect from signals posted from different thread +// so that signals that are already posted from the other thread and not delivered +// yet will be ignored. Unfortunately, simple QObject::disconnect() doesn't +// work like that, since signals that are already posted and are awaiting +// to be delivered WILL BE delivered later, even after a call to QObject::disconnect(). +// The delivery will happen when the control returns to the main event loop. + +// This proxy class solves the above problem. Instead of a call to +// QObject::disconnect(), which would still deliver posted signals, +// we delete the proxy object immediately. In this way signals which are +// already posted and are awaiting to be delivered won't be delivered to the +// destroyed object. + +// So the only reason for this proxy object is to be able to disconnect +// effectively from the signals posted from different threads. + +class VcsCommandResultProxy : public QObject { + Q_OBJECT +public: + VcsCommandResultProxy(VcsCommand *command, VcsBaseDiffEditorControllerPrivate *target); +private: + void storeOutput(const QString &output); + void commandFinished(bool success); + + VcsBaseDiffEditorControllerPrivate *m_target; +}; + +///////////////////// + class VcsBaseDiffEditorControllerPrivate { public: @@ -59,6 +90,7 @@ public: void processingFinished(); void processDiff(const QString &patch); void cancelReload(); + void storeOutput(const QString &output); void commandFinished(bool success); VcsBaseDiffEditorController *q; @@ -67,18 +99,43 @@ public: QString m_startupFile; QString m_output; QPointer<VcsCommand> m_command; - QFutureWatcher<QList<FileData>> m_processWatcher; + QPointer<VcsCommandResultProxy> m_commandResultProxy; + QFutureWatcher<QList<FileData>> *m_processWatcher = nullptr; }; -VcsBaseDiffEditorControllerPrivate::VcsBaseDiffEditorControllerPrivate(VcsBaseDiffEditorController *controller, +///////////////////// + +VcsCommandResultProxy::VcsCommandResultProxy(VcsCommand *command, + VcsBaseDiffEditorControllerPrivate *target) + : QObject(target->q) + , m_target(target) +{ + connect(command, &VcsCommand::stdOutText, + this, &VcsCommandResultProxy::storeOutput); + connect(command, &VcsCommand::finished, + this, &VcsCommandResultProxy::commandFinished); + connect(command, &VcsCommand::destroyed, + this, &QObject::deleteLater); +} + +void VcsCommandResultProxy::storeOutput(const QString &output) +{ + m_target->storeOutput(output); +} + +void VcsCommandResultProxy::commandFinished(bool success) +{ + m_target->commandFinished(success); +} + +VcsBaseDiffEditorControllerPrivate::VcsBaseDiffEditorControllerPrivate( + VcsBaseDiffEditorController *controller, VcsBaseClientImpl *client, const QString &workingDirectory) : q(controller) , m_client(client) , m_directory(workingDirectory) { - QObject::connect(&m_processWatcher, &QFutureWatcher<QList<FileData>>::finished, q, - [this] () { processingFinished(); }); } VcsBaseDiffEditorControllerPrivate::~VcsBaseDiffEditorControllerPrivate() @@ -88,9 +145,18 @@ VcsBaseDiffEditorControllerPrivate::~VcsBaseDiffEditorControllerPrivate() void VcsBaseDiffEditorControllerPrivate::processingFinished() { - bool success = !m_processWatcher.future().isCanceled(); + QTC_ASSERT(m_processWatcher, return); + + // success is false when the user clicked the cancel micro button + // inside the progress indicator + const bool success = !m_processWatcher->future().isCanceled(); const QList<FileData> fileDataList = success - ? m_processWatcher.future().result() : QList<FileData>(); + ? m_processWatcher->future().result() : QList<FileData>(); + + // Prevent direct deletion of m_processWatcher since + // processingFinished() is called directly by the m_processWatcher. + m_processWatcher->deleteLater(); + m_processWatcher = nullptr; q->setDiffFiles(fileDataList, q->workingDirectory(), q->startupFile()); q->reloadFinished(success); @@ -98,39 +164,67 @@ void VcsBaseDiffEditorControllerPrivate::processingFinished() void VcsBaseDiffEditorControllerPrivate::processDiff(const QString &patch) { - m_processWatcher.setFuture(Utils::runAsync(&readPatch, patch)); + cancelReload(); + + m_processWatcher = new QFutureWatcher<QList<FileData>>(); - ProgressManager::addTask(m_processWatcher.future(), - q->tr("Processing diff"), "DiffEditor"); + QObject::connect(m_processWatcher, &QFutureWatcher<QList<FileData>>::finished, + [this] () { processingFinished(); } ); + + m_processWatcher->setFuture(Utils::runAsync(&readPatch, patch)); + + ProgressManager::addTask(m_processWatcher->future(), + q->tr("Processing diff"), "DiffEditor"); } void VcsBaseDiffEditorControllerPrivate::cancelReload() { if (m_command) { - m_command->disconnect(); m_command->cancel(); m_command.clear(); } - if (m_processWatcher.future().isRunning()) { - m_processWatcher.future().cancel(); - m_processWatcher.setFuture(QFuture<QList<FileData>>()); + // Disconnect effectively, don't deliver already posted signals + if (m_commandResultProxy) + delete m_commandResultProxy.data(); + + if (m_processWatcher) { + // Cancel the running process without the further processingFinished() + // notification for this process. + m_processWatcher->future().cancel(); + delete m_processWatcher; + m_processWatcher = nullptr; } + m_output = QString(); } +void VcsBaseDiffEditorControllerPrivate::storeOutput(const QString &output) +{ + m_output = output; +} + void VcsBaseDiffEditorControllerPrivate::commandFinished(bool success) { if (m_command) m_command.clear(); + // Prevent direct deletion of m_commandResultProxy inside the possible + // subsequent synchronous calls to cancelReload() [called e.g. by + // processCommandOutput() overload], since + // commandFinished() is called directly by the m_commandResultProxy. + // m_commandResultProxy is removed via deleteLater right after + // a call to this commandFinished() is finished + if (m_commandResultProxy) + m_commandResultProxy.clear(); + if (!success) { cancelReload(); q->reloadFinished(success); return; } - q->processCommandOutput(m_output); + q->processCommandOutput(QString(m_output)); // pass a copy of m_output } ///////////////////// @@ -150,14 +244,15 @@ VcsBaseDiffEditorController::~VcsBaseDiffEditorController() void VcsBaseDiffEditorController::runCommand(const QList<QStringList> &args, unsigned flags, QTextCodec *codec) { + // Cancel the possible ongoing reload without the commandFinished() nor + // processingFinished() notifications, as right after that + // we re-reload it from scratch. So no intermediate "Retrieving data failed." + // and "Waiting for data..." will be shown. d->cancelReload(); d->m_command = new VcsCommand(workingDirectory(), d->m_client->processEnvironment()); d->m_command->setCodec(codec ? codec : EditorManager::defaultTextCodec()); - connect(d->m_command.data(), &VcsCommand::stdOutText, this, - [this] (const QString &output) { d->m_output = output; }); - connect(d->m_command.data(), &VcsCommand::finished, this, - [this] (bool success) { d->commandFinished(success); }); + d->m_commandResultProxy = new VcsCommandResultProxy(d->m_command.data(), d); d->m_command->addFlags(flags); for (const QStringList &arg : args) { @@ -195,3 +290,5 @@ QString VcsBaseDiffEditorController::startupFile() const } } // namespace VcsBase + +#include "vcsbasediffeditorcontroller.moc" |