diff options
author | Jarek Kobus <jaroslaw.kobus@qt.io> | 2022-08-25 12:56:03 +0200 |
---|---|---|
committer | Jarek Kobus <jaroslaw.kobus@qt.io> | 2022-09-02 13:21:57 +0000 |
commit | 3dc3870475f451a18b18f78bf232b960a775722c (patch) | |
tree | aa38e538fac23eb5cbb9c4783569386c26aa5f42 | |
parent | 210c60a40144bb50b82fd0e5ab81ab44888be9aa (diff) | |
download | qt-creator-3dc3870475f451a18b18f78bf232b960a775722c.tar.gz |
VcsCommand: Don't run in separate thread
Change-Id: Ief0e859d3ce48f804e128cc0f5cb1b390a066923
Reviewed-by: hjk <hjk@qt.io>
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
-rw-r--r-- | src/plugins/vcsbase/vcscommand.cpp | 179 | ||||
-rw-r--r-- | src/plugins/vcsbase/vcscommand.h | 3 | ||||
-rw-r--r-- | src/plugins/vcsbase/vcsplugin.cpp | 9 | ||||
-rw-r--r-- | src/plugins/vcsbase/vcsplugin.h | 1 |
4 files changed, 101 insertions, 91 deletions
diff --git a/src/plugins/vcsbase/vcscommand.cpp b/src/plugins/vcsbase/vcscommand.cpp index 3e9d931469..55020a9ad9 100644 --- a/src/plugins/vcsbase/vcscommand.cpp +++ b/src/plugins/vcsbase/vcscommand.cpp @@ -5,10 +5,8 @@ #include "vcsbaseplugin.h" #include "vcsoutputwindow.h" -#include "vcsplugin.h" #include <coreplugin/icore.h> -#include <coreplugin/progressmanager/futureprogress.h> #include <coreplugin/progressmanager/progressmanager.h> #include <utils/environment.h> @@ -87,6 +85,9 @@ public: void setupSynchronous(QtcProcess *process); bool isFullySynchronous() const; void handleDone(QtcProcess *process); + void startAll(); + void startNextJob(); + void processDone(); VcsCommand *q = nullptr; @@ -96,9 +97,14 @@ public: QTextCodec *m_codec = nullptr; ProgressParser *m_progressParser = nullptr; QFutureWatcher<void> m_watcher; - FutureProgress *m_futureProgress = nullptr; QList<Job> m_jobs; + int m_currentJob = 0; + std::unique_ptr<QtcProcess> m_process; + QString m_stdOut; + QString m_stdErr; + QFutureInterface<void> m_futureInterface; + unsigned m_flags = 0; bool m_progressiveOutput = false; @@ -194,6 +200,73 @@ void VcsCommandPrivate::handleDone(QtcProcess *process) emit q->runCommandFinished(process->workingDirectory()); } +void VcsCommandPrivate::startAll() +{ + // Check that the binary path is not empty + QTC_ASSERT(!m_jobs.isEmpty(), return); + QTC_ASSERT(!m_process, return); + m_futureInterface.reportStarted(); + if (m_flags & VcsCommand::ExpectRepoChanges) { + QMetaObject::invokeMethod(this, [] { + GlobalFileChangeBlocker::instance()->forceBlocked(true); + }); + } + if (m_progressParser) + m_progressParser->setFuture(&m_futureInterface); + else + m_futureInterface.setProgressRange(0, 1); + m_currentJob = 0; + startNextJob(); +} + +void VcsCommandPrivate::startNextJob() +{ + QTC_ASSERT(m_currentJob < m_jobs.count(), return); + m_process.reset(new QtcProcess); + connect(m_process.get(), &QtcProcess::done, this, &VcsCommandPrivate::processDone); + setupProcess(m_process.get(), m_jobs.at(m_currentJob)); + if (!isFullySynchronous()) + setupSynchronous(m_process.get()); + m_process->start(); +} + +void VcsCommandPrivate::processDone() +{ + handleDone(m_process.get()); + m_stdOut += m_process->cleanedStdOut(); + m_stdErr += m_process->cleanedStdErr(); + ++m_currentJob; + const bool success = m_process->result() == ProcessResult::FinishedWithSuccess; + if (m_currentJob < m_jobs.count() && success) { + m_process.release()->deleteLater(); + startNextJob(); + return; + } + if (m_flags & VcsCommand::ExpectRepoChanges) { + QMetaObject::invokeMethod(this, [] { + GlobalFileChangeBlocker::instance()->forceBlocked(false); + }); + } + if (m_aborted) { + m_futureInterface.reportCanceled(); + m_futureInterface.reportFinished(); + } else { + if (!m_progressiveOutput) { + emit q->stdOutText(m_stdOut); + if (!m_stdErr.isEmpty()) + emit q->stdErrText(m_stdErr); + } + emit q->finished(success); + if (!success) + m_futureInterface.reportCanceled(); + m_futureInterface.reportFinished(); + } + if (m_progressParser) + m_progressParser->setFuture(nullptr); + // As it is used asynchronously, we need to delete ourselves + q->deleteLater(); +} + } // namespace Internal VcsCommand::VcsCommand(const FilePath &workingDirectory, const Environment &environment) : @@ -218,17 +291,6 @@ VcsCommand::VcsCommand(const FilePath &workingDirectory, const Environment &envi }); } -void VcsCommand::addTask(const QFuture<void> &future) -{ - if ((d->m_flags & VcsCommand::SuppressCommandLogging)) - return; - - const QString name = d->displayName(); - const auto id = Id::fromString(name + QLatin1String(".action")); - d->m_futureProgress = ProgressManager::addTask(future, name, id); - Internal::VcsPlugin::addFuture(future); -} - void VcsCommand::postRunCommand(const FilePath &workingDirectory) { if (!(d->m_flags & VcsCommand::ExpectRepoChanges)) @@ -240,8 +302,10 @@ void VcsCommand::postRunCommand(const FilePath &workingDirectory) VcsCommand::~VcsCommand() { - if (!d->m_watcher.future().isFinished()) - d->m_watcher.future().cancel(); + if (d->m_futureInterface.isRunning()) { + d->m_futureInterface.reportCanceled(); + d->m_futureInterface.reportFinished(); + } delete d; } @@ -259,6 +323,7 @@ void VcsCommand::addJob(const CommandLine &command, int timeoutS, const FilePath &workingDirectory, const ExitCodeInterpreter &interpreter) { + QTC_ASSERT(!command.executable().isEmpty(), return); d->m_jobs.push_back({command, timeoutS, !workingDirectory.isEmpty() ? workingDirectory : d->m_defaultWorkingDirectory, interpreter}); } @@ -268,9 +333,17 @@ void VcsCommand::execute() if (d->m_jobs.empty()) return; - QFuture<void> task = runAsync(&VcsCommand::run, this); - d->m_watcher.setFuture(task); - addTask(task); + d->startAll(); + d->m_watcher.setFuture(d->m_futureInterface.future()); + if ((d->m_flags & VcsCommand::SuppressCommandLogging)) + return; + + const QString name = d->displayName(); + const auto id = Id::fromString(name + QLatin1String(".action")); + if (d->m_progressParser) + ProgressManager::addTask(d->m_futureInterface.future(), name, id); + else + ProgressManager::addTimedTask(d->m_futureInterface, name, id, qMax(2, d->timeoutS() / 5)); } void VcsCommand::abort() @@ -281,66 +354,16 @@ void VcsCommand::abort() void VcsCommand::cancel() { - emit terminate(); -} - -void VcsCommand::run(QFutureInterface<void> &future) -{ - // Check that the binary path is not empty - QTC_ASSERT(!d->m_jobs.isEmpty(), return); - - QString stdOut; - QString stdErr; - - if (d->m_flags & VcsCommand::ExpectRepoChanges) { - QMetaObject::invokeMethod(this, [] { - GlobalFileChangeBlocker::instance()->forceBlocked(true); - }); - } - if (d->m_progressParser) { - d->m_progressParser->setFuture(&future); - } else { - QMetaObject::invokeMethod(this, [this, future] { - (void) new ProgressTimer(future, qMax(2, d->timeoutS() / 5), d->m_futureProgress); - }); - } - - const int count = d->m_jobs.size(); - bool lastExecSuccess = true; - for (int j = 0; j < count; j++) { - const Internal::VcsCommandPrivate::Job &job = d->m_jobs.at(j); - const CommandResult result = runCommand(job.command, job.timeoutS, - job.workingDirectory, job.exitCodeInterpreter); - stdOut += result.cleanedStdOut(); - stdErr += result.cleanedStdErr(); - lastExecSuccess = result.result() == ProcessResult::FinishedWithSuccess; - if (!lastExecSuccess) - break; - } - - if (!d->m_aborted) { - if (!d->m_progressiveOutput) { - emit stdOutText(stdOut); - if (!stdErr.isEmpty()) - emit stdErrText(stdErr); - } - - if (d->m_flags & VcsCommand::ExpectRepoChanges) { - QMetaObject::invokeMethod(this, [] { - GlobalFileChangeBlocker::instance()->forceBlocked(false); - }); - } - emit finished(lastExecSuccess); - if (lastExecSuccess) - future.setProgressValue(future.progressMaximum()); - else - future.cancel(); // sets the progress indicator red + d->m_futureInterface.reportCanceled(); + if (d->m_process) { + // TODO: we may want to call cancel here... + d->m_process->stop(); + // TODO: we may want to not wait here... + // However, VcsBaseDiffEditorController::runCommand() relies on getting finished() signal + d->m_process->waitForFinished(); + d->m_process.reset(); } - - if (d->m_progressParser) - d->m_progressParser->setFuture(nullptr); - // As it is used asynchronously, we need to delete ourselves - this->deleteLater(); + emit terminate(); } CommandResult VcsCommand::runCommand(const Utils::CommandLine &command, int timeoutS) diff --git a/src/plugins/vcsbase/vcscommand.h b/src/plugins/vcsbase/vcscommand.h index bb60d63b70..0cd6ea36ea 100644 --- a/src/plugins/vcsbase/vcscommand.h +++ b/src/plugins/vcsbase/vcscommand.h @@ -45,7 +45,6 @@ private: QFutureInterface<void> *m_future; QMutex *m_futureMutex = nullptr; - friend class VcsCommand; friend class Internal::VcsCommandPrivate; }; @@ -134,14 +133,12 @@ signals: void runCommandFinished(const Utils::FilePath &workingDirectory); private: - void run(QFutureInterface<void> &future); // This is called once per job in a thread. // When called from the UI thread it will execute fully synchronously, so no signals will // be triggered! CommandResult runCommand(const Utils::CommandLine &command, int timeoutS, const Utils::FilePath &workingDirectory, const Utils::ExitCodeInterpreter &interpreter); - void addTask(const QFuture<void> &future); void postRunCommand(const Utils::FilePath &workingDirectory); // Run without a event loop in fully blocking mode. No signals will be delivered. diff --git a/src/plugins/vcsbase/vcsplugin.cpp b/src/plugins/vcsbase/vcsplugin.cpp index 70edca64de..dd81f84a55 100644 --- a/src/plugins/vcsbase/vcsplugin.cpp +++ b/src/plugins/vcsbase/vcsplugin.cpp @@ -22,7 +22,6 @@ #include <projectexplorer/project.h> #include <projectexplorer/projecttree.h> -#include <utils/futuresynchronizer.h> #include <utils/macroexpander.h> #include <utils/qtcassert.h> @@ -40,7 +39,6 @@ class VcsPluginPrivate public: CommonOptionsPage m_settingsPage; QStandardItemModel *m_nickNameModel = nullptr; - FutureSynchronizer m_synchronizer; }; static VcsPlugin *m_instance = nullptr; @@ -53,7 +51,6 @@ VcsPlugin::VcsPlugin() VcsPlugin::~VcsPlugin() { QTC_ASSERT(d, return); - d->m_synchronizer.waitForFinished(); VcsOutputWindow::destroy(); m_instance = nullptr; delete d; @@ -65,7 +62,6 @@ bool VcsPlugin::initialize(const QStringList &arguments, QString *errorMessage) Q_UNUSED(errorMessage) d = new VcsPluginPrivate; - d->m_synchronizer.setCancelOnWait(true); EditorManager::addCloseEditorListener([this](IEditor *editor) -> bool { bool result = true; @@ -124,11 +120,6 @@ VcsPlugin *VcsPlugin::instance() return m_instance; } -void VcsPlugin::addFuture(const QFuture<void> &future) -{ - m_instance->d->m_synchronizer.addFuture(future); -} - CommonVcsSettings &VcsPlugin::settings() const { return d->m_settingsPage.settings(); diff --git a/src/plugins/vcsbase/vcsplugin.h b/src/plugins/vcsbase/vcsplugin.h index f58b8f4d2a..162d090ed0 100644 --- a/src/plugins/vcsbase/vcsplugin.h +++ b/src/plugins/vcsbase/vcsplugin.h @@ -31,7 +31,6 @@ public: bool initialize(const QStringList &arguments, QString *errorMessage) override; static VcsPlugin *instance(); - static void addFuture(const QFuture<void> &future); CommonVcsSettings &settings() const; |