File indexing completed on 2024-11-24 04:54:54

0001 /*
0002  *   SPDX-FileCopyrightText: 2013 Aleix Pol Gonzalez <aleixpol@blue-systems.com>
0003  *   SPDX-FileCopyrightText: 2017 Jan Grulich <jgrulich@redhat.com>
0004  *   SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
0005  *
0006  *   SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL
0007  */
0008 
0009 #include "FlatpakJobTransaction.h"
0010 #include "FlatpakBackend.h"
0011 #include "FlatpakResource.h"
0012 #include "FlatpakTransactionThread.h"
0013 
0014 #include <thread>
0015 
0016 #include <QDebug>
0017 #include <QScopeGuard>
0018 #include <QTimer>
0019 
0020 namespace
0021 {
0022 class ThreadPool : public QThreadPool
0023 {
0024 public:
0025     ThreadPool()
0026     {
0027         // Cap the amount of concurrency to prevent too many in-flight transactions. This in particular
0028         // prevents running out of file descriptors or other limited resources.
0029         // https://bugs.kde.org/show_bug.cgi?id=474231
0030         constexpr auto arbitraryMaxConcurrency = 4U;
0031         const auto concurrency = std::min(std::thread::hardware_concurrency(), arbitraryMaxConcurrency);
0032         setMaxThreadCount(static_cast<int>(concurrency));
0033     }
0034 };
0035 } // namespace
0036 
0037 Q_GLOBAL_STATIC(ThreadPool, s_pool);
0038 
0039 FlatpakJobTransaction::FlatpakJobTransaction(FlatpakResource *app, Role role, bool delayStart)
0040     : Transaction(app->backend(), app, role, {})
0041     , m_app(app)
0042 {
0043     setCancellable(true);
0044     setStatus(QueuedStatus);
0045 
0046     if (!delayStart) {
0047         QTimer::singleShot(0, this, &FlatpakJobTransaction::start);
0048     }
0049 }
0050 
0051 FlatpakJobTransaction::~FlatpakJobTransaction()
0052 {
0053     cancel();
0054     if (s_pool->tryTake(m_appJob)) { // immediately delete if the runnable hasn't started yet
0055         delete m_appJob;
0056     } else { // otherwise defer cleanup to the pool
0057         m_appJob->setAutoDelete(true);
0058     }
0059 }
0060 
0061 void FlatpakJobTransaction::cancel()
0062 {
0063     m_appJob->cancel();
0064 }
0065 
0066 void FlatpakJobTransaction::start()
0067 {
0068     setStatus(CommittingStatus);
0069 
0070     // App job will be added every time
0071     m_appJob = new FlatpakTransactionThread(m_app, role());
0072     m_appJob->setAutoDelete(false);
0073     connect(m_appJob, &FlatpakTransactionThread::finished, this, &FlatpakJobTransaction::finishTransaction);
0074     connect(m_appJob, &FlatpakTransactionThread::progressChanged, this, &FlatpakJobTransaction::setProgress);
0075     connect(m_appJob, &FlatpakTransactionThread::speedChanged, this, &FlatpakJobTransaction::setDownloadSpeed);
0076     connect(m_appJob, &FlatpakTransactionThread::passiveMessage, this, &FlatpakJobTransaction::passiveMessage);
0077     connect(m_appJob, &FlatpakTransactionThread::webflowStarted, this, &FlatpakJobTransaction::webflowStarted);
0078     connect(m_appJob, &FlatpakTransactionThread::webflowDone, this, &FlatpakJobTransaction::webflowDone);
0079 
0080     s_pool->start(m_appJob);
0081 }
0082 
0083 void FlatpakJobTransaction::finishTransaction()
0084 {
0085     if (static_cast<FlatpakBackend *>(m_app->backend())->getInstalledRefForApp(m_app)) {
0086         m_app->setState(AbstractResource::Installed);
0087     } else {
0088         m_app->setState(AbstractResource::None);
0089     }
0090 
0091     if (!m_appJob->addedRepositories().isEmpty()) {
0092         Q_EMIT repositoriesAdded(m_appJob->addedRepositories());
0093     }
0094 
0095     if (!m_appJob->cancelled() && !m_appJob->errorMessage().isEmpty()) {
0096         Q_EMIT passiveMessage(m_appJob->errorMessage());
0097     }
0098 
0099     if (m_appJob->result()) {
0100         setStatus(DoneStatus);
0101     } else {
0102         setStatus(m_appJob->cancelled() ? CancelledStatus : DoneWithErrorStatus);
0103     }
0104 }