From 2d001a3d8d4cde6264e9073e8d586d1e9855abd8 Mon Sep 17 00:00:00 2001 From: Arttu Tarkiainen Date: Wed, 26 Oct 2022 12:46:51 +0300 Subject: Metadatajob: update cache asynchronously Refactor the updating to run in a separate thread, with QFuture -based handling of the result. This removes the visible blocking of UI while updating the cache. Task-number: QTIFW-2815 Change-Id: I67bc1aed8465ca12925b9f80effadf4be7017cea Reviewed-by: Katja Marttila --- src/libs/installer/metadatajob.cpp | 76 ++++++++++++++------------------------ src/libs/installer/metadatajob.h | 4 +- src/libs/installer/metadatajob_p.h | 75 +++++++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+), 49 deletions(-) diff --git a/src/libs/installer/metadatajob.cpp b/src/libs/installer/metadatajob.cpp index 659648e1a..c28770192 100644 --- a/src/libs/installer/metadatajob.cpp +++ b/src/libs/installer/metadatajob.cpp @@ -100,6 +100,7 @@ MetadataJob::MetadataJob(QObject *parent) connect(&m_xmlTask, &QFutureWatcherBase::finished, this, &MetadataJob::xmlTaskFinished); connect(&m_metadataTask, &QFutureWatcherBase::finished, this, &MetadataJob::metadataTaskFinished); connect(&m_metadataTask, &QFutureWatcherBase::progressValueChanged, this, &MetadataJob::progressChanged); + connect(&m_updateCacheTask, &QFutureWatcherBase::finished, this, &MetadataJob::updateCacheTaskFinished); } MetadataJob::~MetadataJob() @@ -337,47 +338,15 @@ void MetadataJob::startUnzipRepositoryTask(const Repository &repo) watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task)); } -bool MetadataJob::updateCache() +void MetadataJob::startUpdateCacheTask() { const int toRegisterCount = m_fetchedMetadata.count(); if (toRegisterCount > 0) emit infoMessage(this, tr("Updating local cache with %n new items...", nullptr, toRegisterCount)); - // Register items from current run to cache - QStringList registeredKeys; - bool success = true; - for (auto *meta : qAsConst(m_fetchedMetadata)) { - if (!m_metaFromCache.registerItem(meta, true)) { - success = false; - break; - } - meta->setPersistentRepositoryPath(meta->repository().url()); - registeredKeys.append(m_fetchedMetadata.key(meta)); - } - // Remove items whose ownership was transferred to cache - for (auto &key : qAsConst(registeredKeys)) - m_fetchedMetadata.remove(key); - - // Bail out if there was error while registering items - if (!success) { - emitFinishedWithError(QInstaller::CacheError, m_metaFromCache.errorString() + u' ' - + tr("Clearing the cache directory and restarting the application may solve this.")); - m_metaFromCache.sync(); - return false; - } - - // ...and clean up obsolete cached items - const QList obsolete = m_metaFromCache.obsoleteItems(); - for (auto *meta : obsolete) - m_metaFromCache.removeItem(meta->checksum()); - - if (!m_metaFromCache.sync()) { - emitFinishedWithError(QInstaller::CacheError, m_metaFromCache.errorString() + u' ' - + tr("Clearing the cache directory and restarting the application may solve this.")); - return false; - } - return true; + UpdateCacheTask *task = new UpdateCacheTask(m_metaFromCache, m_fetchedMetadata); + m_updateCacheTask.setFuture(QtConcurrent::run(&UpdateCacheTask::doTask, task)); } /* @@ -544,12 +513,10 @@ void MetadataJob::xmlTaskFinished() if (!fetchMetaDataPackages()) { // No new metadata packages to fetch, still need to update the cache // for refreshed repositories. - if (updateCache()) - emitFinished(); + startUpdateCacheTask(); } } else { - if (updateCache()) - emitFinished(); + startUpdateCacheTask(); } } else if (status == XmlDownloadRetry) { QMetaObject::invokeMethod(this, "doStart", Qt::QueuedConnection); @@ -579,13 +546,8 @@ void MetadataJob::unzipTaskFinished() m_unzipTasks.remove(watcher); delete watcher; - if (m_unzipTasks.isEmpty()) { - if (!updateCache()) - return; - - setProcessedAmount(100); - emitFinished(); - } + if (m_unzipTasks.isEmpty()) + startUpdateCacheTask(); } void MetadataJob::progressChanged(int progress) @@ -627,8 +589,7 @@ void MetadataJob::metadataTaskFinished() watcher->setFuture(QtConcurrent::run(&UnzipArchiveTask::doTask, task)); } } else { - if (updateCache()) - emitFinished(); + startUpdateCacheTask(); } } } catch (const TaskException &e) { @@ -643,6 +604,25 @@ void MetadataJob::metadataTaskFinished() } } +void MetadataJob::updateCacheTaskFinished() +{ + try { + m_updateCacheTask.waitForFinished(); + } catch (const CacheTaskException &e) { + emitFinishedWithError(QInstaller::CacheError, e.message()); + } catch (const QUnhandledException &e) { + emitFinishedWithError(QInstaller::CacheError, QLatin1String(e.what())); + } catch (...) { + emitFinishedWithError(QInstaller::CacheError, tr("Unknown exception during updating cache.")); + } + + if (error() != Job::NoError) + return; + + setProcessedAmount(100); + emitFinished(); +} + // -- private diff --git a/src/libs/installer/metadatajob.h b/src/libs/installer/metadatajob.h index c0412b9cd..9e8116124 100644 --- a/src/libs/installer/metadatajob.h +++ b/src/libs/installer/metadatajob.h @@ -83,6 +83,7 @@ private slots: void xmlTaskFinished(); void unzipTaskFinished(); void metadataTaskFinished(); + void updateCacheTaskFinished(); void progressChanged(int progress); void setProgressTotalAmount(int maximum); void unzipRepositoryTaskFinished(); @@ -91,7 +92,7 @@ private slots: private: bool fetchMetaDataPackages(); void startUnzipRepositoryTask(const Repository &repo); - bool updateCache(); + void startUpdateCacheTask(); void resetCacheRepositories(); void reset(); void resetCompressedFetch(); @@ -117,6 +118,7 @@ private: TempPathDeleter m_tempDirDeleter; QFutureWatcher m_xmlTask; QFutureWatcher m_metadataTask; + QFutureWatcher m_updateCacheTask; QHash *, QObject*> m_unzipTasks; QHash *, QObject*> m_unzipRepositoryTasks; DownloadType m_downloadType; diff --git a/src/libs/installer/metadatajob_p.h b/src/libs/installer/metadatajob_p.h index 2456a971e..5a4fcad2d 100644 --- a/src/libs/installer/metadatajob_p.h +++ b/src/libs/installer/metadatajob_p.h @@ -99,6 +99,81 @@ private: QString m_targetDir; }; +class CacheTaskException : public QException +{ +public: + CacheTaskException() {} + explicit CacheTaskException(const QString &message) + : m_message(message) + {} + ~CacheTaskException() {} + + void raise() const override { throw *this; } + QString message() const { return m_message; } + CacheTaskException *clone() const override { return new CacheTaskException(*this); } + +private: + QString m_message; +}; + +class UpdateCacheTask : public AbstractTask +{ + Q_OBJECT + Q_DISABLE_COPY(UpdateCacheTask) + +public: + UpdateCacheTask(GenericDataCache &cache, QHash &updates) + : m_cache(&cache) + , m_updates(&updates) + {} + + void doTask(QFutureInterface &fi) override + { + fi.reportStarted(); + fi.setExpectedResultCount(1); + + // Register items from current run to cache + QStringList registeredKeys; + bool success = true; + for (auto *meta : qAsConst(*m_updates)) { + if (!m_cache->registerItem(meta, true)) { + success = false; + break; + } + meta->setPersistentRepositoryPath(meta->repository().url()); + registeredKeys.append(m_updates->key(meta)); + } + // Remove items whose ownership was transferred to cache + for (auto &key : qAsConst(registeredKeys)) + m_updates->remove(key); + + // Bail out if there was error while registering items + if (!success) { + fi.reportException(UnzipArchiveException(m_cache->errorString() + u' ' + + MetadataJob::tr("Clearing the cache directory and restarting the application may solve this."))); + m_cache->sync(); + fi.reportFinished(); + return; + } + + // ...and clean up obsolete cached items + const QList obsolete = m_cache->obsoleteItems(); + for (auto *meta : obsolete) + m_cache->removeItem(meta->checksum()); + + if (!m_cache->sync()) { + fi.reportException(UnzipArchiveException(m_cache->errorString() + u' ' + + MetadataJob::tr("Clearing the cache directory and restarting the application may solve this."))); + } + + fi.reportFinished(); + } + +private: + GenericDataCache *const m_cache; + QHash *const m_updates; +}; + } // namespace QInstaller #endif // METADATAJOB_P_H -- cgit v1.2.3