diff --git a/alpm/manager.cpp b/alpm/manager.cpp index 4a10334..e5f2976 100644 --- a/alpm/manager.cpp +++ b/alpm/manager.cpp @@ -572,10 +572,14 @@ const QJsonObject &Manager::basicRepoInfo() const QMutexLocker locker(&m_basicRepoInfoMutex); if(m_basicRepoInfo.isEmpty()) { // add local data base - m_basicRepoInfo.insert(localDataBase()->name(), localDataBase()->basicInfo()); + { + QReadLocker locker(localDataBase()->lock()); + m_basicRepoInfo.insert(localDataBase()->name(), localDataBase()->basicInfo()); + } // add sync data bases for(const auto &syncDb : syncDatabases()) { // check if the "sync" database is actually used for syncing + QReadLocker locker(syncDb.second->lock()); auto usage = syncDb.second->usage(); if((usage & ALPM_DB_USAGE_SYNC) || (usage & ALPM_DB_USAGE_INSTALL) || (usage & ALPM_DB_USAGE_UPGRADE)) { m_basicRepoInfo.insert(syncDb.first, syncDb.second->basicInfo()); @@ -585,6 +589,7 @@ const QJsonObject &Manager::basicRepoInfo() const } // add AUR if(userRepository()) { + QReadLocker locker(userRepository()->lock()); m_basicRepoInfo.insert(userRepository()->name(), userRepository()->basicInfo()); } } diff --git a/alpm/packageinfolookup.cpp b/alpm/packageinfolookup.cpp index bfd4ce3..152414e 100644 --- a/alpm/packageinfolookup.cpp +++ b/alpm/packageinfolookup.cpp @@ -37,6 +37,7 @@ PackageInfoLookup::PackageInfoLookup(Manager &manager, const QJsonObject &reques } } if(!packagesToBeRequested.isEmpty()) { + QReadLocker locker(repo->lock()); if(const auto *reply = (m_part & Manager::Details ? repo->requestFullPackageInfo(packagesToBeRequested) : repo->requestPackageInfo(packagesToBeRequested))) { connect(reply, &PackageReply::resultsAvailable, this, &PackageInfoLookup::addResultsFromReply); ++m_remainingReplies; @@ -69,7 +70,7 @@ void PackageInfoLookup::addResultsDirectly(const QStringList &packageNames, cons } bool avail = false; try { - if(const auto &pkg = packages.at(packageName)) { + if(const auto *pkg = packages.at(packageName)) { avail = true; if(m_part & Manager::Basics) { res.insert(QStringLiteral("basics"), pkg->basicInfo()); @@ -93,22 +94,22 @@ void PackageInfoLookup::addResultsFromReply() auto *reply = static_cast(sender()); reply->deleteLater(); if(reply->error().isEmpty()) { + QReadLocker lock(reply->repository()->lock()); addResultsDirectly(reply->requestedPackages(), reply->repository()); - if(!--m_remainingReplies) { - emit resultsAvailable(m_what, m_id, m_results); - deleteLater(); - } } else { - // TODO: report error - /* + // TODO: bunch error messages together for(const auto &packageName : packageNames) { QJsonObject res; res.insert(QStringLiteral("name"), packageName); res.insert(QStringLiteral("repo"), repo->name()); + res.insert(QStringLiteral("error"), reply->error()); + m_results << res; } - */ } - + if(!--m_remainingReplies) { + emit resultsAvailable(m_what, m_id, m_results); + deleteLater(); + } } } // namespace RepoIndex diff --git a/alpm/repository.cpp b/alpm/repository.cpp index 8da3025..4bb2cee 100644 --- a/alpm/repository.cpp +++ b/alpm/repository.cpp @@ -78,7 +78,8 @@ Repository::Repository(const QString &name, uint32 index, QObject *parent) : m_index(index), m_name(name), m_usage(static_cast(0)), - m_sigLevel(static_cast(ALPM_SIGSTATUS_INVALID)) + m_sigLevel(static_cast(ALPM_SIGSTATUS_INVALID)), + m_lock(QReadWriteLock::Recursive) {} /*! diff --git a/alpm/repository.h b/alpm/repository.h index c48c840..6c65cda 100644 --- a/alpm/repository.h +++ b/alpm/repository.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include @@ -82,6 +83,7 @@ class SuggestionsReply : public Reply Q_OBJECT public: SuggestionsReply(QNetworkReply *networkReply, const QString &term, Repository *repo); + const Repository *repository() const; QJsonObject suggestions() const; protected: @@ -95,6 +97,11 @@ inline SuggestionsReply::SuggestionsReply(QNetworkReply *networkReply, const QSt m_repo(repo) {} +inline const Repository *SuggestionsReply::repository() const +{ + return m_repo; +} + /*! * \brief The RepositoryType enum specifies the type of a repository object. */ @@ -198,6 +205,9 @@ public: // parsing src/pkg info QList addPackagesFromSrcInfo(const QByteArray &srcInfo); + // thread synchronization + QReadWriteLock *lock() const; + static const uint32 invalidIndex = static_cast(-1); protected: @@ -215,6 +225,9 @@ protected: QList m_upgradeSources; QString m_srcDir; QString m_pkgDir; + +private: + QReadWriteLock m_lock; }; /*! @@ -401,6 +414,11 @@ inline bool Repository::isCachingUseful() const } } +inline QReadWriteLock *Repository::lock() const +{ + return const_cast(&m_lock); +} + } // namespace PackageManagement #endif // PACKAGEMANAGEMENT_PACKAGESOURCE_H diff --git a/alpm/resolvebuildorder.cpp b/alpm/resolvebuildorder.cpp index ce4ba41..5548085 100644 --- a/alpm/resolvebuildorder.cpp +++ b/alpm/resolvebuildorder.cpp @@ -154,8 +154,11 @@ QStringList BuildOrderResolver::resolve(const StringVector &packages) const tasks << new TaskInfo(QString::fromLocal8Bit(pkgName.data())); } // find specified packages and their dependencies - for(int i = 0, size = tasks.size(); i != size; ++i) { - addDeps(tasks, tasks.at(i)); + //for(int i = 0, size = tasks.size(); i != size; ++i) { + // addDeps(tasks, tasks.at(i)); + //} + for(auto *task : tasks) { + addDeps(tasks, task); } if(m_manager.config().isVerbose()) { cerr << shchar << "Relevant packages: "; diff --git a/alpm/suggestionslookup.cpp b/alpm/suggestionslookup.cpp index 6a9b57d..37a742f 100644 --- a/alpm/suggestionslookup.cpp +++ b/alpm/suggestionslookup.cpp @@ -24,13 +24,13 @@ SuggestionsLookup::SuggestionsLookup(Manager &manager, const QJsonObject &reques if(m_errors.isEmpty()) { for(const auto &repoName : repos) { if(auto *repo = manager.repositoryByName(repoName.toString())) { + QReadLocker locker(repo->lock()); if(const auto *reply = repo->requestSuggestions(searchTerm)) { connect(reply, &SuggestionsReply::resultsAvailable, this, &SuggestionsLookup::addResults); ++m_remainingReplies; } else { m_results << repo->suggestions(searchTerm); } - } else { m_errors << QStringLiteral("The specified repository \"%1\" does not exist.").arg(repoName.toString()); } @@ -44,7 +44,10 @@ void SuggestionsLookup::addResults() { assert(m_remainingReplies); auto *reply = static_cast(sender()); - m_results << reply->suggestions(); + { + QReadLocker locker(reply->repository()->lock()); + m_results << reply->suggestions(); + } reply->deleteLater(); if(!--m_remainingReplies) { emit resultsAvailable(QStringLiteral("suggestions"), m_id, m_results); diff --git a/alpm/upgradelookup.cpp b/alpm/upgradelookup.cpp index 9727d30..222104b 100644 --- a/alpm/upgradelookup.cpp +++ b/alpm/upgradelookup.cpp @@ -42,6 +42,7 @@ QJsonObject UpgradeResult::toJson() const /*! * \brief Constructs a new upgrade lookup process. The upgrade lookup process is started immediately. + * \remarks \a upgradeLookup and \a upgradeSource musted be locked by caller */ UpgradeLookupProcess::UpgradeLookupProcess(UpgradeLookup *upgradeLookup, Repository *upgradeSource) : QObject(upgradeLookup), @@ -51,19 +52,21 @@ UpgradeLookupProcess::UpgradeLookupProcess(UpgradeLookup *upgradeLookup, Reposit m_watcher(new QFutureWatcher(this)) { connect(this, &UpgradeLookupProcess::finished, upgradeLookup, &UpgradeLookup::processFinished); - switch(m_upgradeSource->requestsRequired()) { - case PackageDetailAvailability::Request: - m_reply = m_upgradeSource->requestPackageInfo(m_toCheck->packageNames()); - break; - case PackageDetailAvailability::FullRequest: - m_reply = m_upgradeSource->requestFullPackageInfo(m_toCheck->packageNames()); - break; - case PackageDetailAvailability::Never: - m_results.errors << QStringLiteral("Repository \"%1\" does not provide the required information.").arg(m_upgradeSource->name()); - emit finished(); - return; - case PackageDetailAvailability::Immediately: - break; + { + switch(m_upgradeSource->requestsRequired()) { + case PackageDetailAvailability::Request: + m_reply = m_upgradeSource->requestPackageInfo(m_toCheck->packageNames()); + break; + case PackageDetailAvailability::FullRequest: + m_reply = m_upgradeSource->requestFullPackageInfo(m_toCheck->packageNames()); + break; + case PackageDetailAvailability::Never: + m_results.errors << QStringLiteral("Repository \"%1\" does not provide the required information.").arg(m_upgradeSource->name()); + emit finished(); + return; + case PackageDetailAvailability::Immediately: + break; + } } if(m_reply) { m_reply->setParent(this); @@ -76,7 +79,7 @@ UpgradeLookupProcess::UpgradeLookupProcess(UpgradeLookup *upgradeLookup, Reposit /*! * \brief Returns the results. Results are available, after the finished() signal has been emitted. */ -const UpgradeLookupResults &UpgradeLookupProcess::results() const +inline const UpgradeLookupResults &UpgradeLookupProcess::results() const { return m_results; } @@ -101,6 +104,7 @@ void UpgradeLookupProcess::sourceReady() */ void UpgradeLookupProcess::checkUpgrades() { + QReadLocker toCheckLocker(m_toCheck->lock()), srcLocker(m_upgradeSource->lock()); m_toCheck->checkForUpgrades(m_results, QList() << m_upgradeSource); } @@ -171,10 +175,12 @@ UpgradeLookupJson::UpgradeLookupJson(Manager &manager, const QJsonObject &reques { const auto toCheckName = request.value(QStringLiteral("db")).toString(); if((m_toCheck = manager.repositoryByName(toCheckName))) { + QReadLocker toCheckLocker(m_toCheck->lock()); // construct upgrade lookup processes const auto syncDbsArray = request.value(QStringLiteral("syncdbs")).toArray(); if(syncDbsArray.isEmpty()) { for(auto *src : m_toCheck->upgradeSources()) { + QReadLocker srcLocker(src->lock()); new UpgradeLookupProcess(this, src); ++m_remainingProcesses; } @@ -182,6 +188,7 @@ UpgradeLookupJson::UpgradeLookupJson(Manager &manager, const QJsonObject &reques for(const auto &syncDbValue : syncDbsArray) { const auto syncDbName = syncDbValue.toString(); if(auto *src = manager.repositoryByName(syncDbName)) { + QReadLocker srcLocker(src->lock()); new UpgradeLookupProcess(this, src); ++m_remainingProcesses; } else { @@ -273,8 +280,10 @@ UpgradeLookupCli::UpgradeLookupCli(Manager &manager, const string &repo, QObject cerr << shchar << "Checking upgrades for \"" << repo << "\" ..." << endl; const auto toCheckName = qstr(repo); if((m_toCheck = manager.repositoryByName(toCheckName))) { + QReadLocker toCheckLocker(m_toCheck->lock()); // construct upgrade lookup processes for(auto *src : m_toCheck->upgradeSources()) { + QReadLocker srcLocker(src->lock()); new UpgradeLookupProcess(this, src); ++m_remainingProcesses; } diff --git a/network/userrepository.cpp b/network/userrepository.cpp index ef99235..68ccb22 100644 --- a/network/userrepository.cpp +++ b/network/userrepository.cpp @@ -49,12 +49,10 @@ void AurPackageReply::processData() auto *reply = m_networkReplies.front(); if(reply->error() == QNetworkReply::NoError) { QJsonParseError error; - //QByteArray data = m_networkReply->readAll(); - //cerr << shchar << "AUR reply: " << data.data() << endl; - //const QJsonDocument doc = QJsonDocument::fromJson(data, &error); const auto doc = QJsonDocument::fromJson(reply->readAll(), &error); - auto &packages = m_repo->packages(); if(error.error == QJsonParseError::NoError) { + QWriteLocker locker(m_repo->lock()); + auto &packages = m_repo->packages(); for(const auto &result : doc.object().value(QStringLiteral("results")).toArray()) { QJsonObject obj = result.toObject(); QString packageName = obj.value(QStringLiteral("Name")).toString(); @@ -68,10 +66,10 @@ void AurPackageReply::processData() } } } else { - m_error = QStringLiteral("Error: Unable to parse JSON received from AUR: ") % error.errorString() % QStringLiteral(" at character ") % QString::number(error.offset); + m_error = QStringLiteral("Unable to parse JSON received from AUR: ") % error.errorString() % QStringLiteral(" at character ") % QString::number(error.offset); } } else { - m_error = QStringLiteral("Error: Unable to request data from AUR via AurJson: ") + reply->errorString(); + m_error = QStringLiteral("Unable to request data from AUR via AurJson: ") + reply->errorString(); } emit resultsAvailable(); } @@ -112,6 +110,7 @@ void AurFullPackageReply::processData() } if(srcInfoEntry && srcInfoEntry->isFile()) { const auto srcInfo = static_cast(srcInfoEntry)->data(); + QWriteLocker locker(m_userRepo->lock()); const auto packages = m_userRepo->addPackagesFromSrcInfo(srcInfo); // TODO: error handling for(const auto &entryName : baseDir->entries()) { @@ -129,13 +128,13 @@ void AurFullPackageReply::processData() } } } else { - m_error = QStringLiteral("Error: Aur tarball does not contain \".SRCINFO\"."); + m_error = QStringLiteral("Aur tarball does not contain \".SRCINFO\"."); } } else { - m_error = QStringLiteral("Error: Unable to open tarball reply."); + m_error = QStringLiteral("Unable to open tarball reply."); } } else { - m_error = QStringLiteral("Error: Unable to request tarball from AUR: ") + reply->errorString(); + m_error = QStringLiteral("Unable to request tarball from AUR: ") + reply->errorString(); } if(!m_error.isEmpty()) { qDebug() << m_error; @@ -157,6 +156,7 @@ void AurSuggestionsReply::processData() //const QJsonDocument doc = QJsonDocument::fromJson(data, &error); const auto doc = QJsonDocument::fromJson(reply->readAll(), &error); if(error.error == QJsonParseError::NoError) { + QWriteLocker locker(m_repo->lock()); auto &packages = m_repo->packages(); if(doc.isObject()) { for(const auto &result : doc.object().value(QStringLiteral("results")).toArray()) { @@ -183,10 +183,10 @@ void AurSuggestionsReply::processData() } } } else { - m_error = QStringLiteral("Error: Unable to parse JSON received from AUR: ") % error.errorString() % QStringLiteral(" at character ") % QString::number(error.offset); + m_error = QStringLiteral("Unable to parse JSON received from AUR: ") % error.errorString() % QStringLiteral(" at character ") % QString::number(error.offset); } } else { - m_error = QStringLiteral("Error: Unable to request data from AUR: ") + reply->errorString(); + m_error = QStringLiteral("Unable to request data from AUR: ") + reply->errorString(); } emit resultsAvailable(); }