aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp')
-rw-r--r--src/plugins/vcsbase/vcsbasediffeditorcontroller.cpp133
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"