Browse Source

Handle tracker status updates asynchronously

* Add a helper for performing jobs in Session context
* Handle tracker status updates asynchronously

PR #18010.
adaptive-webui-19844
Vladimir Golovnev 2 years ago committed by GitHub
parent
commit
1b2ff0f6f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      src/base/bittorrent/session.h
  2. 74
      src/base/bittorrent/sessionimpl.cpp
  3. 13
      src/base/bittorrent/sessionimpl.h
  4. 49
      src/base/bittorrent/torrentimpl.cpp
  5. 9
      src/base/bittorrent/torrentimpl.h
  6. 77
      src/gui/transferlistfilterswidget.cpp
  7. 6
      src/gui/transferlistfilterswidget.h

2
src/base/bittorrent/session.h

@ -468,6 +468,6 @@ namespace BitTorrent
void trackersRemoved(Torrent *torrent, const QStringList &trackers); void trackersRemoved(Torrent *torrent, const QStringList &trackers);
void trackerSuccess(Torrent *torrent, const QString &tracker); void trackerSuccess(Torrent *torrent, const QString &tracker);
void trackerWarning(Torrent *torrent, const QString &tracker); void trackerWarning(Torrent *torrent, const QString &tracker);
void trackerEntriesUpdated(const QHash<Torrent *, QSet<QString>> &updateInfos); void trackerEntriesUpdated(Torrent *torrent, const QHash<QString, TrackerEntry> &updatedTrackerEntries);
}; };
} }

74
src/base/bittorrent/sessionimpl.cpp

@ -1,6 +1,6 @@
/* /*
* Bittorrent Client using Qt and libtorrent. * Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2015 Vladimir Golovnev <glassez@yandex.ru> * Copyright (C) 2015-2022 Vladimir Golovnev <glassez@yandex.ru>
* Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org> * Copyright (C) 2006 Christophe Dumez <chris@qbittorrent.org>
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
@ -35,7 +35,6 @@
#include <ctime> #include <ctime>
#include <queue> #include <queue>
#include <string> #include <string>
#include <utility>
#ifdef Q_OS_WIN #ifdef Q_OS_WIN
#include <Windows.h> #include <Windows.h>
@ -118,6 +117,24 @@ const Path CATEGORIES_FILE_NAME {u"categories.json"_qs};
const int MAX_PROCESSING_RESUMEDATA_COUNT = 50; const int MAX_PROCESSING_RESUMEDATA_COUNT = 50;
const int STATISTICS_SAVE_INTERVAL = std::chrono::milliseconds(15min).count(); const int STATISTICS_SAVE_INTERVAL = std::chrono::milliseconds(15min).count();
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
namespace std
{
uint qHash(const std::string &key, uint seed = 0)
{
return qHash(QByteArray::fromRawData(key.data(), static_cast<int>(key.length())), seed);
}
}
namespace libtorrent
{
uint qHash(const libtorrent::torrent_handle &key)
{
return static_cast<uint>(libtorrent::hash_value(key));
}
}
#endif
namespace namespace
{ {
const char PEER_ID[] = "qB"; const char PEER_ID[] = "qB";
@ -5809,13 +5826,12 @@ void SessionImpl::handleTrackerAlert(const lt::tracker_alert *a)
if (!torrent) if (!torrent)
return; return;
const auto trackerURL = QString::fromUtf8(a->tracker_url()); QMap<TrackerEntry::Endpoint, int> &updateInfo = m_updatedTrackerEntries[torrent->nativeHandle()][std::string(a->tracker_url())];
m_updatedTrackerEntries[torrent].insert(trackerURL);
if (a->type() == lt::tracker_reply_alert::alert_type) if (a->type() == lt::tracker_reply_alert::alert_type)
{ {
const int numPeers = static_cast<const lt::tracker_reply_alert *>(a)->num_peers; const int numPeers = static_cast<const lt::tracker_reply_alert *>(a)->num_peers;
torrent->updatePeerCount(trackerURL, a->local_endpoint, numPeers); updateInfo.insert(a->local_endpoint, numPeers);
} }
} }
@ -5869,20 +5885,50 @@ void SessionImpl::handleTorrentConflictAlert(const lt::torrent_conflict_alert *a
void SessionImpl::processTrackerStatuses() void SessionImpl::processTrackerStatuses()
{ {
if (m_updatedTrackerEntries.isEmpty())
return;
for (auto it = m_updatedTrackerEntries.cbegin(); it != m_updatedTrackerEntries.cend(); ++it) for (auto it = m_updatedTrackerEntries.cbegin(); it != m_updatedTrackerEntries.cend(); ++it)
{ {
auto torrent = static_cast<TorrentImpl *>(it.key()); invokeAsync([this, torrentHandle = it.key(), updatedTrackers = it.value()]() mutable
const QSet<QString> &updatedTrackers = it.value(); {
try
{
std::vector<lt::announce_entry> nativeTrackers = torrentHandle.trackers();
invoke([this, torrentHandle, nativeTrackers = std::move(nativeTrackers)
, updatedTrackers = std::move(updatedTrackers)]
{
TorrentImpl *torrent = m_torrents.value(torrentHandle.info_hash());
if (!torrent)
return;
for (const QString &trackerURL : updatedTrackers) QHash<QString, TrackerEntry> updatedTrackerEntries;
torrent->invalidateTrackerEntry(trackerURL); updatedTrackerEntries.reserve(updatedTrackers.size());
} for (const lt::announce_entry &announceEntry : nativeTrackers)
{
const auto updatedTrackersIter = updatedTrackers.find(announceEntry.url);
if (updatedTrackersIter == updatedTrackers.end())
continue;
if (!m_updatedTrackerEntries.isEmpty()) const QMap<TrackerEntry::Endpoint, int> &updateInfo = updatedTrackersIter.value();
{ TrackerEntry trackerEntry = torrent->updateTrackerEntry(announceEntry, updateInfo);
emit trackerEntriesUpdated(m_updatedTrackerEntries); #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
m_updatedTrackerEntries.clear(); updatedTrackerEntries[trackerEntry.url] = std::move(trackerEntry);
#else
updatedTrackerEntries.emplace(trackerEntry.url, std::move(trackerEntry));
#endif
}
emit trackerEntriesUpdated(torrent, updatedTrackerEntries);
});
}
catch (const std::exception &)
{
}
});
} }
m_updatedTrackerEntries.clear();
} }
void SessionImpl::saveStatistics() const void SessionImpl::saveStatistics() const

13
src/base/bittorrent/sessionimpl.h

@ -29,6 +29,7 @@
#pragma once #pragma once
#include <utility>
#include <variant> #include <variant>
#include <vector> #include <vector>
@ -36,11 +37,11 @@
#include <libtorrent/portmap.hpp> #include <libtorrent/portmap.hpp>
#include <libtorrent/torrent_handle.hpp> #include <libtorrent/torrent_handle.hpp>
#include <QtContainerFwd>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QHash> #include <QHash>
#include <QPointer> #include <QPointer>
#include <QSet> #include <QSet>
#include <QtContainerFwd>
#include <QVector> #include <QVector>
#include "base/path.h" #include "base/path.h"
@ -438,6 +439,12 @@ namespace BitTorrent
void addMappedPorts(const QSet<quint16> &ports); void addMappedPorts(const QSet<quint16> &ports);
void removeMappedPorts(const QSet<quint16> &ports); void removeMappedPorts(const QSet<quint16> &ports);
template <typename Func>
void invoke(Func &&func)
{
QMetaObject::invokeMethod(this, std::forward<Func>(func));
}
void invokeAsync(std::function<void ()> func); void invokeAsync(std::function<void ()> func);
private slots: private slots:
@ -719,7 +726,9 @@ namespace BitTorrent
QMap<QString, CategoryOptions> m_categories; QMap<QString, CategoryOptions> m_categories;
QSet<QString> m_tags; QSet<QString> m_tags;
QHash<Torrent *, QSet<QString>> m_updatedTrackerEntries; // This field holds amounts of peers reported by trackers in their responses to announces
// (torrent.tracker_name.tracker_local_endpoint.num_peers)
QHash<lt::torrent_handle, QHash<std::string, QMap<TrackerEntry::Endpoint, int>>> m_updatedTrackerEntries;
// I/O errored torrents // I/O errored torrents
QSet<TorrentID> m_recentErroredTorrents; QSet<TorrentID> m_recentErroredTorrents;

49
src/base/bittorrent/torrentimpl.cpp

@ -82,10 +82,10 @@ namespace
#ifdef QBT_USES_LIBTORRENT2 #ifdef QBT_USES_LIBTORRENT2
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
, const lt::info_hash_t &hashes, const QMap<TrackerEntry::Endpoint, int> &updateInfo) , const lt::info_hash_t &hashes, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
#else #else
void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry void updateTrackerEntry(TrackerEntry &trackerEntry, const lt::announce_entry &nativeEntry
, const QMap<TrackerEntry::Endpoint, int> &updateInfo) , const QMap<TrackerEntry::Endpoint, int> &updateInfo)
#endif #endif
{ {
Q_ASSERT(trackerEntry.url == QString::fromStdString(nativeEntry.url)); Q_ASSERT(trackerEntry.url == QString::fromStdString(nativeEntry.url));
@ -522,9 +522,6 @@ void TorrentImpl::setAutoManaged(const bool enable)
QVector<TrackerEntry> TorrentImpl::trackers() const QVector<TrackerEntry> TorrentImpl::trackers() const
{ {
if (!m_updatedTrackerEntries.isEmpty())
refreshTrackerEntries();
return m_trackerEntries; return m_trackerEntries;
} }
@ -1515,43 +1512,25 @@ void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileN
endReceivedMetadataHandling(savePath, fileNames); endReceivedMetadataHandling(savePath, fileNames);
} }
void TorrentImpl::updatePeerCount(const QString &trackerURL, const TrackerEntry::Endpoint &endpoint, const int count) TrackerEntry TorrentImpl::updateTrackerEntry(const lt::announce_entry &announceEntry, const QMap<TrackerEntry::Endpoint, int> &updateInfo)
{
m_updatedTrackerEntries[trackerURL][endpoint] = count;
}
void TorrentImpl::invalidateTrackerEntry(const QString &trackerURL)
{
std::ignore = m_updatedTrackerEntries[trackerURL];
}
void TorrentImpl::refreshTrackerEntries() const
{ {
const std::vector<lt::announce_entry> nativeTrackers = m_nativeHandle.trackers(); const auto it = std::find_if(m_trackerEntries.begin(), m_trackerEntries.end()
Q_ASSERT(nativeTrackers.size() == m_trackerEntries.size()); , [&announceEntry](const TrackerEntry &trackerEntry)
for (TrackerEntry &trackerEntry : m_trackerEntries)
{ {
const auto updatedTrackerIter = m_updatedTrackerEntries.find(trackerEntry.url); return (trackerEntry.url == QString::fromStdString(announceEntry.url));
if (updatedTrackerIter == m_updatedTrackerEntries.end()) });
continue;
const auto nativeTrackerIter = std::find_if(nativeTrackers.cbegin(), nativeTrackers.cend() Q_ASSERT(it != m_trackerEntries.end());
, [trackerURL = trackerEntry.url.toStdString()](const lt::announce_entry &announceEntry) // TODO: use [[unlikely]] in C++20
{ if (Q_UNLIKELY(it == m_trackerEntries.end()))
return (announceEntry.url == trackerURL); return {};
});
Q_ASSERT(nativeTrackerIter != nativeTrackers.cend());
const lt::announce_entry &announceEntry = *nativeTrackerIter;
#ifdef QBT_USES_LIBTORRENT2 #ifdef QBT_USES_LIBTORRENT2
updateTrackerEntry(trackerEntry, announceEntry, m_nativeHandle.info_hashes(), updatedTrackerIter.value()); ::updateTrackerEntry(*it, announceEntry, nativeHandle().info_hashes(), updateInfo);
#else #else
updateTrackerEntry(trackerEntry, announceEntry, updatedTrackerIter.value()); ::updateTrackerEntry(*it, announceEntry, updateInfo);
#endif #endif
} return *it;
m_updatedTrackerEntries.clear();
} }
std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const std::shared_ptr<const libtorrent::torrent_info> TorrentImpl::nativeTorrentInfo() const

9
src/base/bittorrent/torrentimpl.h

@ -248,15 +248,13 @@ namespace BitTorrent
void saveResumeData(lt::resume_data_flags_t flags = {}); void saveResumeData(lt::resume_data_flags_t flags = {});
void handleMoveStorageJobFinished(const Path &path, bool hasOutstandingJob); void handleMoveStorageJobFinished(const Path &path, bool hasOutstandingJob);
void fileSearchFinished(const Path &savePath, const PathList &fileNames); void fileSearchFinished(const Path &savePath, const PathList &fileNames);
void updatePeerCount(const QString &trackerURL, const TrackerEntry::Endpoint &endpoint, int count); TrackerEntry updateTrackerEntry(const lt::announce_entry &announceEntry, const QMap<TrackerEntry::Endpoint, int> &updateInfo);
void invalidateTrackerEntry(const QString &trackerURL);
private: private:
using EventTrigger = std::function<void ()>; using EventTrigger = std::function<void ()>;
std::shared_ptr<const lt::torrent_info> nativeTorrentInfo() const; std::shared_ptr<const lt::torrent_info> nativeTorrentInfo() const;
void refreshTrackerEntries() const;
void updateStatus(const lt::torrent_status &nativeStatus); void updateStatus(const lt::torrent_status &nativeStatus);
void updateState(); void updateState();
@ -316,10 +314,7 @@ namespace BitTorrent
MaintenanceJob m_maintenanceJob = MaintenanceJob::None; MaintenanceJob m_maintenanceJob = MaintenanceJob::None;
// TODO: Use QHash<TrackerEntry::Endpoint, int> once Qt5 is dropped. QVector<TrackerEntry> m_trackerEntries;
using TrackerEntryUpdateInfo = QMap<TrackerEntry::Endpoint, int>;
mutable QHash<QString, TrackerEntryUpdateInfo> m_updatedTrackerEntries;
mutable QVector<TrackerEntry> m_trackerEntries;
FileErrorInfo m_lastFileError; FileErrorInfo m_lastFileError;
// Persistent data // Persistent data

77
src/gui/transferlistfilterswidget.cpp

@ -599,60 +599,52 @@ void TrackerFiltersList::setDownloadTrackerFavicon(bool value)
} }
} }
void TrackerFiltersList::handleTrackerEntriesUpdated(const QHash<BitTorrent::Torrent *, QSet<QString>> &updateInfos) void TrackerFiltersList::handleTrackerEntriesUpdated(const BitTorrent::Torrent *torrent
, const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackerEntries)
{ {
for (auto torrentsIt = updateInfos.cbegin(); torrentsIt != updateInfos.cend(); ++torrentsIt) const BitTorrent::TorrentID id = torrent->id();
{
const BitTorrent::Torrent *torrent = torrentsIt.key();
const QSet<QString> &trackerURLs = torrentsIt.value();
const BitTorrent::TorrentID id = torrent->id();
auto errorHashesIt = m_errors.find(id); auto errorHashesIt = m_errors.find(id);
auto warningHashesIt = m_warnings.find(id); auto warningHashesIt = m_warnings.find(id);
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers(); for (const BitTorrent::TrackerEntry &trackerEntry : updatedTrackerEntries)
for (const BitTorrent::TrackerEntry &trackerEntry : trackers) {
if (trackerEntry.status == BitTorrent::TrackerEntry::Working)
{ {
if (!trackerURLs.contains(trackerEntry.url)) if (errorHashesIt != m_errors.end())
continue;
if (trackerEntry.status == BitTorrent::TrackerEntry::Working)
{ {
if (errorHashesIt != m_errors.end()) QSet<QString> &errored = errorHashesIt.value();
{ errored.remove(trackerEntry.url);
QSet<QString> &errored = errorHashesIt.value(); }
errored.remove(trackerEntry.url);
}
if (trackerEntry.message.isEmpty()) if (trackerEntry.message.isEmpty())
{ {
if (warningHashesIt != m_warnings.end()) if (warningHashesIt != m_warnings.end())
{
QSet<QString> &warned = *warningHashesIt;
warned.remove(trackerEntry.url);
}
}
else
{ {
if (warningHashesIt == m_warnings.end()) QSet<QString> &warned = *warningHashesIt;
warningHashesIt = m_warnings.insert(id, {}); warned.remove(trackerEntry.url);
warningHashesIt.value().insert(trackerEntry.url);
} }
} }
else if (trackerEntry.status == BitTorrent::TrackerEntry::NotWorking) else
{ {
if (errorHashesIt == m_errors.end()) if (warningHashesIt == m_warnings.end())
errorHashesIt = m_errors.insert(id, {}); warningHashesIt = m_warnings.insert(id, {});
errorHashesIt.value().insert(trackerEntry.url); warningHashesIt.value().insert(trackerEntry.url);
} }
} }
else if (trackerEntry.status == BitTorrent::TrackerEntry::NotWorking)
if ((errorHashesIt != m_errors.end()) && errorHashesIt.value().isEmpty()) {
m_errors.erase(errorHashesIt); if (errorHashesIt == m_errors.end())
if ((warningHashesIt != m_warnings.end()) && warningHashesIt.value().isEmpty()) errorHashesIt = m_errors.insert(id, {});
m_warnings.erase(warningHashesIt); errorHashesIt.value().insert(trackerEntry.url);
}
} }
if ((errorHashesIt != m_errors.end()) && errorHashesIt.value().isEmpty())
m_errors.erase(errorHashesIt);
if ((warningHashesIt != m_warnings.end()) && warningHashesIt.value().isEmpty())
m_warnings.erase(warningHashesIt);
item(ERROR_ROW)->setText(tr("Error (%1)").arg(m_errors.size())); item(ERROR_ROW)->setText(tr("Error (%1)").arg(m_errors.size()));
item(WARNING_ROW)->setText(tr("Warning (%1)").arg(m_warnings.size())); item(WARNING_ROW)->setText(tr("Warning (%1)").arg(m_warnings.size()));
@ -920,9 +912,10 @@ void TransferListFiltersWidget::changeTrackerless(const BitTorrent::Torrent *tor
m_trackerFilters->changeTrackerless(torrent, trackerless); m_trackerFilters->changeTrackerless(torrent, trackerless);
} }
void TransferListFiltersWidget::trackerEntriesUpdated(const QHash<BitTorrent::Torrent *, QSet<QString>> &updateInfos) void TransferListFiltersWidget::trackerEntriesUpdated(const BitTorrent::Torrent *torrent
, const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackerEntries)
{ {
m_trackerFilters->handleTrackerEntriesUpdated(updateInfos); m_trackerFilters->handleTrackerEntriesUpdated(torrent, updatedTrackerEntries);
} }
void TransferListFiltersWidget::onCategoryFilterStateChanged(bool enabled) void TransferListFiltersWidget::onCategoryFilterStateChanged(bool enabled)

6
src/gui/transferlistfilterswidget.h

@ -131,7 +131,8 @@ public:
void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers); void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers);
void refreshTrackers(const BitTorrent::Torrent *torrent); void refreshTrackers(const BitTorrent::Torrent *torrent);
void changeTrackerless(const BitTorrent::Torrent *torrent, bool trackerless); void changeTrackerless(const BitTorrent::Torrent *torrent, bool trackerless);
void handleTrackerEntriesUpdated(const QHash<BitTorrent::Torrent *, QSet<QString>> &updateInfos); void handleTrackerEntriesUpdated(const BitTorrent::Torrent *torrent
, const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackerEntries);
void setDownloadTrackerFavicon(bool value); void setDownloadTrackerFavicon(bool value);
private slots: private slots:
@ -180,7 +181,8 @@ public slots:
void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers); void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers);
void refreshTrackers(const BitTorrent::Torrent *torrent); void refreshTrackers(const BitTorrent::Torrent *torrent);
void changeTrackerless(const BitTorrent::Torrent *torrent, bool trackerless); void changeTrackerless(const BitTorrent::Torrent *torrent, bool trackerless);
void trackerEntriesUpdated(const QHash<BitTorrent::Torrent *, QSet<QString>> &updateInfos); void trackerEntriesUpdated(const BitTorrent::Torrent *torrent
, const QHash<QString, BitTorrent::TrackerEntry> &updatedTrackerEntries);
private slots: private slots:
void onCategoryFilterStateChanged(bool enabled); void onCategoryFilterStateChanged(bool enabled);

Loading…
Cancel
Save