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 @@ -468,6 +468,6 @@ namespace BitTorrent
void trackersRemoved(Torrent *torrent, const QStringList &trackers);
void trackerSuccess(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 @@ @@ -1,6 +1,6 @@
/*
* 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>
*
* This program is free software; you can redistribute it and/or
@ -35,7 +35,6 @@ @@ -35,7 +35,6 @@
#include <ctime>
#include <queue>
#include <string>
#include <utility>
#ifdef Q_OS_WIN
#include <Windows.h>
@ -118,6 +117,24 @@ const Path CATEGORIES_FILE_NAME {u"categories.json"_qs}; @@ -118,6 +117,24 @@ const Path CATEGORIES_FILE_NAME {u"categories.json"_qs};
const int MAX_PROCESSING_RESUMEDATA_COUNT = 50;
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
{
const char PEER_ID[] = "qB";
@ -5809,13 +5826,12 @@ void SessionImpl::handleTrackerAlert(const lt::tracker_alert *a) @@ -5809,13 +5826,12 @@ void SessionImpl::handleTrackerAlert(const lt::tracker_alert *a)
if (!torrent)
return;
const auto trackerURL = QString::fromUtf8(a->tracker_url());
m_updatedTrackerEntries[torrent].insert(trackerURL);
QMap<TrackerEntry::Endpoint, int> &updateInfo = m_updatedTrackerEntries[torrent->nativeHandle()][std::string(a->tracker_url())];
if (a->type() == lt::tracker_reply_alert::alert_type)
{
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 @@ -5869,20 +5885,50 @@ void SessionImpl::handleTorrentConflictAlert(const lt::torrent_conflict_alert *a
void SessionImpl::processTrackerStatuses()
{
if (m_updatedTrackerEntries.isEmpty())
return;
for (auto it = m_updatedTrackerEntries.cbegin(); it != m_updatedTrackerEntries.cend(); ++it)
{
auto torrent = static_cast<TorrentImpl *>(it.key());
const QSet<QString> &updatedTrackers = it.value();
invokeAsync([this, torrentHandle = it.key(), updatedTrackers = it.value()]() mutable
{
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)
torrent->invalidateTrackerEntry(trackerURL);
}
QHash<QString, TrackerEntry> updatedTrackerEntries;
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())
{
emit trackerEntriesUpdated(m_updatedTrackerEntries);
m_updatedTrackerEntries.clear();
const QMap<TrackerEntry::Endpoint, int> &updateInfo = updatedTrackersIter.value();
TrackerEntry trackerEntry = torrent->updateTrackerEntry(announceEntry, updateInfo);
#if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
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

13
src/base/bittorrent/sessionimpl.h

@ -29,6 +29,7 @@ @@ -29,6 +29,7 @@
#pragma once
#include <utility>
#include <variant>
#include <vector>
@ -36,11 +37,11 @@ @@ -36,11 +37,11 @@
#include <libtorrent/portmap.hpp>
#include <libtorrent/torrent_handle.hpp>
#include <QtContainerFwd>
#include <QElapsedTimer>
#include <QHash>
#include <QPointer>
#include <QSet>
#include <QtContainerFwd>
#include <QVector>
#include "base/path.h"
@ -438,6 +439,12 @@ namespace BitTorrent @@ -438,6 +439,12 @@ namespace BitTorrent
void addMappedPorts(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);
private slots:
@ -719,7 +726,9 @@ namespace BitTorrent @@ -719,7 +726,9 @@ namespace BitTorrent
QMap<QString, CategoryOptions> m_categories;
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
QSet<TorrentID> m_recentErroredTorrents;

49
src/base/bittorrent/torrentimpl.cpp

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

9
src/base/bittorrent/torrentimpl.h

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

77
src/gui/transferlistfilterswidget.cpp

@ -599,60 +599,52 @@ void TrackerFiltersList::setDownloadTrackerFavicon(bool value) @@ -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::Torrent *torrent = torrentsIt.key();
const QSet<QString> &trackerURLs = torrentsIt.value();
const BitTorrent::TorrentID id = torrent->id();
const BitTorrent::TorrentID id = torrent->id();
auto errorHashesIt = m_errors.find(id);
auto warningHashesIt = m_warnings.find(id);
auto errorHashesIt = m_errors.find(id);
auto warningHashesIt = m_warnings.find(id);
const QVector<BitTorrent::TrackerEntry> trackers = torrent->trackers();
for (const BitTorrent::TrackerEntry &trackerEntry : trackers)
for (const BitTorrent::TrackerEntry &trackerEntry : updatedTrackerEntries)
{
if (trackerEntry.status == BitTorrent::TrackerEntry::Working)
{
if (!trackerURLs.contains(trackerEntry.url))
continue;
if (trackerEntry.status == BitTorrent::TrackerEntry::Working)
if (errorHashesIt != m_errors.end())
{
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 (warningHashesIt != m_warnings.end())
{
QSet<QString> &warned = *warningHashesIt;
warned.remove(trackerEntry.url);
}
}
else
if (trackerEntry.message.isEmpty())
{
if (warningHashesIt != m_warnings.end())
{
if (warningHashesIt == m_warnings.end())
warningHashesIt = m_warnings.insert(id, {});
warningHashesIt.value().insert(trackerEntry.url);
QSet<QString> &warned = *warningHashesIt;
warned.remove(trackerEntry.url);
}
}
else if (trackerEntry.status == BitTorrent::TrackerEntry::NotWorking)
else
{
if (errorHashesIt == m_errors.end())
errorHashesIt = m_errors.insert(id, {});
errorHashesIt.value().insert(trackerEntry.url);
if (warningHashesIt == m_warnings.end())
warningHashesIt = m_warnings.insert(id, {});
warningHashesIt.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);
else if (trackerEntry.status == BitTorrent::TrackerEntry::NotWorking)
{
if (errorHashesIt == m_errors.end())
errorHashesIt = m_errors.insert(id, {});
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(WARNING_ROW)->setText(tr("Warning (%1)").arg(m_warnings.size()));
@ -920,9 +912,10 @@ void TransferListFiltersWidget::changeTrackerless(const BitTorrent::Torrent *tor @@ -920,9 +912,10 @@ void TransferListFiltersWidget::changeTrackerless(const BitTorrent::Torrent *tor
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)

6
src/gui/transferlistfilterswidget.h

@ -131,7 +131,8 @@ public: @@ -131,7 +131,8 @@ public:
void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers);
void refreshTrackers(const BitTorrent::Torrent *torrent);
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);
private slots:
@ -180,7 +181,8 @@ public slots: @@ -180,7 +181,8 @@ public slots:
void removeTrackers(const BitTorrent::Torrent *torrent, const QStringList &trackers);
void refreshTrackers(const BitTorrent::Torrent *torrent);
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:
void onCategoryFilterStateChanged(bool enabled);

Loading…
Cancel
Save