From 732b2bcbdb80debe1622dfe78dfd5530f2b0d008 Mon Sep 17 00:00:00 2001 From: Vladimir Golovnev Date: Sun, 27 Apr 2025 16:24:07 +0300 Subject: [PATCH] Provide asynchronous results via QFuture Makes asynchronous logic to look more straightforward. Allows caller to choose blocking or non-blocking way of obtaining asynchronous results via the same interface. PR #22598. --- src/base/bittorrent/filesearcher.cpp | 24 ++-- src/base/bittorrent/filesearcher.h | 21 ++- src/base/bittorrent/sessionimpl.cpp | 87 ++++++------ src/base/bittorrent/sessionimpl.h | 8 +- src/base/bittorrent/torrent.h | 15 +-- src/base/bittorrent/torrentcontenthandler.h | 7 +- src/base/bittorrent/torrentimpl.cpp | 142 +++++--------------- src/base/bittorrent/torrentimpl.h | 19 +-- src/gui/addnewtorrentdialog.cpp | 15 ++- src/gui/properties/peerlistwidget.cpp | 5 +- src/gui/properties/propertieswidget.cpp | 16 +-- src/gui/torrentcontentmodel.cpp | 6 +- src/gui/trackerlist/trackerlistmodel.cpp | 5 +- src/webui/api/synccontroller.cpp | 5 +- src/webui/api/torrentscontroller.cpp | 13 +- 15 files changed, 165 insertions(+), 223 deletions(-) diff --git a/src/base/bittorrent/filesearcher.cpp b/src/base/bittorrent/filesearcher.cpp index 2fcd31f8f..9b1a1f9cc 100644 --- a/src/base/bittorrent/filesearcher.cpp +++ b/src/base/bittorrent/filesearcher.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020 Vladimir Golovnev + * Copyright (C) 2020-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -27,13 +27,14 @@ */ #include "filesearcher.h" -#include "base/bittorrent/common.h" -#include "base/bittorrent/infohash.h" -void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &originalFileNames - , const Path &savePath, const Path &downloadPath, const bool forceAppendExt) +#include + +#include "base/bittorrent/common.h" + +namespace { - const auto findInDir = [](const Path &dirPath, PathList &fileNames, const bool forceAppendExt) -> bool + bool findInDir(const Path &dirPath, PathList &fileNames, const bool forceAppendExt) { bool found = false; for (Path &fileName : fileNames) @@ -58,7 +59,13 @@ void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &origi } return found; - }; + } +} + +void FileSearcher::search(const PathList &originalFileNames, const Path &savePath + , const Path &downloadPath, const bool forceAppendExt, QPromise promise) +{ + promise.start(); Path usedPath = savePath; PathList adjustedFileNames = originalFileNames; @@ -69,5 +76,6 @@ void FileSearcher::search(const BitTorrent::TorrentID &id, const PathList &origi findInDir(usedPath, adjustedFileNames, forceAppendExt); } - emit searchFinished(id, usedPath, adjustedFileNames); + promise.addResult(FileSearchResult {.savePath = usedPath, .fileNames = adjustedFileNames}); + promise.finish(); } diff --git a/src/base/bittorrent/filesearcher.h b/src/base/bittorrent/filesearcher.h index 04495bd76..cfd7ada73 100644 --- a/src/base/bittorrent/filesearcher.h +++ b/src/base/bittorrent/filesearcher.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2020 Vladimir Golovnev + * Copyright (C) 2020-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,10 +32,13 @@ #include "base/path.h" -namespace BitTorrent +template class QPromise; + +struct FileSearchResult { - class TorrentID; -} + Path savePath; + PathList fileNames; +}; class FileSearcher final : public QObject { @@ -43,12 +46,8 @@ class FileSearcher final : public QObject Q_DISABLE_COPY_MOVE(FileSearcher) public: - FileSearcher() = default; + using QObject::QObject; -public slots: - void search(const BitTorrent::TorrentID &id, const PathList &originalFileNames - , const Path &savePath, const Path &downloadPath, bool forceAppendExt); - -signals: - void searchFinished(const BitTorrent::TorrentID &id, const Path &savePath, const PathList &fileNames); + void search(const PathList &originalFileNames, const Path &savePath + , const Path &downloadPath, bool forceAppendExt, QPromise promise); }; diff --git a/src/base/bittorrent/sessionimpl.cpp b/src/base/bittorrent/sessionimpl.cpp index e88b7cd5c..d9827b150 100644 --- a/src/base/bittorrent/sessionimpl.cpp +++ b/src/base/bittorrent/sessionimpl.cpp @@ -61,6 +61,7 @@ #include #include #include +#include #include #include #include @@ -69,6 +70,7 @@ #include #include #include +#include #include #include #include @@ -622,7 +624,6 @@ SessionImpl::SessionImpl(QObject *parent) m_fileSearcher = new FileSearcher; m_fileSearcher->moveToThread(m_ioThread.get()); connect(m_ioThread.get(), &QThread::finished, m_fileSearcher, &QObject::deleteLater); - connect(m_fileSearcher, &FileSearcher::searchFinished, this, &SessionImpl::fileSearchFinished); m_torrentContentRemover = new TorrentContentRemover; m_torrentContentRemover->moveToThread(m_ioThread.get()); @@ -2376,31 +2377,6 @@ void SessionImpl::processTorrentShareLimits(TorrentImpl *torrent) } } -void SessionImpl::fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames) -{ - TorrentImpl *torrent = m_torrents.value(id); - if (torrent) - { - torrent->fileSearchFinished(savePath, fileNames); - return; - } - - const auto loadingTorrentsIter = m_loadingTorrents.find(id); - if (loadingTorrentsIter != m_loadingTorrents.end()) - { - LoadTorrentParams ¶ms = loadingTorrentsIter.value(); - lt::add_torrent_params &p = params.ltAddTorrentParams; - - p.save_path = savePath.toString().toStdString(); - const TorrentInfo torrentInfo {*p.ti}; - const auto nativeIndexes = torrentInfo.nativeIndexes(); - for (int i = 0; i < fileNames.size(); ++i) - p.renamed_files[nativeIndexes[i]] = fileNames[i].toString().toStdString(); - - m_nativeSession->async_add_torrent(p); - } -} - void SessionImpl::torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage) { if (errorMessage.isEmpty()) @@ -2828,11 +2804,12 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr lt::add_torrent_params &p = loadTorrentParams.ltAddTorrentParams; p = source.ltAddTorrentParams(); - bool isFindingIncompleteFiles = false; - const bool useAutoTMM = loadTorrentParams.useAutoTMM; const Path actualSavePath = useAutoTMM ? categorySavePath(loadTorrentParams.category) : loadTorrentParams.savePath; + bool needFindIncompleteFiles = false; + PathList filePaths; + if (hasMetadata) { // Torrent that is being added with metadata is considered to be added as stopped @@ -2847,7 +2824,7 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr Q_ASSERT(addTorrentParams.filePaths.isEmpty() || (addTorrentParams.filePaths.size() == torrentInfo.filesCount())); - PathList filePaths = addTorrentParams.filePaths; + filePaths = addTorrentParams.filePaths; if (filePaths.isEmpty()) { filePaths = torrentInfo.filePaths(); @@ -2893,13 +2870,9 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr if (!loadTorrentParams.hasFinishedStatus) { - const Path actualDownloadPath = useAutoTMM - ? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath; - findIncompleteFiles(torrentInfo, actualSavePath, actualDownloadPath, filePaths); - isFindingIncompleteFiles = true; + needFindIncompleteFiles = true; } - - if (!isFindingIncompleteFiles) + else { for (int index = 0; index < filePaths.size(); ++index) p.renamed_files[nativeIndexes[index]] = filePaths.at(index).toString().toStdString(); @@ -2998,23 +2971,49 @@ bool SessionImpl::addTorrent_impl(const TorrentDescriptor &source, const AddTorr m_loadingTorrents.insert(id, loadTorrentParams); if (infoHash.isHybrid()) m_hybridTorrentsByAltID.insert(altID, nullptr); - if (!isFindingIncompleteFiles) + + if (needFindIncompleteFiles) + { + const Path actualDownloadPath = useAutoTMM + ? categoryDownloadPath(loadTorrentParams.category) : loadTorrentParams.downloadPath; + findIncompleteFiles(actualSavePath, actualDownloadPath, filePaths).then(this + , [this, id](const FileSearchResult &result) + { + const auto loadingTorrentsIter = m_loadingTorrents.find(id); + Q_ASSERT(loadingTorrentsIter != m_loadingTorrents.end()); + if (loadingTorrentsIter == m_loadingTorrents.end()) [[unlikely]] + return; + + LoadTorrentParams ¶ms = loadingTorrentsIter.value(); + lt::add_torrent_params &p = params.ltAddTorrentParams; + + p.save_path = result.savePath.toString().toStdString(); + const TorrentInfo torrentInfo {*p.ti}; + const auto nativeIndexes = torrentInfo.nativeIndexes(); + for (int i = 0; i < result.fileNames.size(); ++i) + p.renamed_files[nativeIndexes[i]] = result.fileNames[i].toString().toStdString(); + + m_nativeSession->async_add_torrent(p); + }); + } + else + { m_nativeSession->async_add_torrent(p); + } return true; } -void SessionImpl::findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath - , const Path &downloadPath, const PathList &filePaths) const +QFuture SessionImpl::findIncompleteFiles(const Path &savePath, const Path &downloadPath, const PathList &filePaths) const { - Q_ASSERT(filePaths.isEmpty() || (filePaths.size() == torrentInfo.filesCount())); - - const auto searchId = TorrentID::fromInfoHash(torrentInfo.infoHash()); - const PathList originalFileNames = (filePaths.isEmpty() ? torrentInfo.filePaths() : filePaths); - QMetaObject::invokeMethod(m_fileSearcher, [=, this] + QPromise promise; + QFuture future = promise.future(); + QMetaObject::invokeMethod(m_fileSearcher, [=, this, promise = std::move(promise)]() mutable { - m_fileSearcher->search(searchId, originalFileNames, savePath, downloadPath, isAppendExtensionEnabled()); + m_fileSearcher->search(filePaths, savePath, downloadPath, isAppendExtensionEnabled(), std::move(promise)); }); + + return future; } void SessionImpl::enablePortMapping() diff --git a/src/base/bittorrent/sessionimpl.h b/src/base/bittorrent/sessionimpl.h index 1ad2108ff..e878cf4a6 100644 --- a/src/base/bittorrent/sessionimpl.h +++ b/src/base/bittorrent/sessionimpl.h @@ -61,12 +61,16 @@ class QString; class QTimer; class QUrl; +template class QFuture; + class BandwidthScheduler; class FileSearcher; class FilterParserThread; class FreeDiskSpaceChecker; class NativeSessionExtension; +struct FileSearchResult; + namespace BitTorrent { enum class MoveStorageMode; @@ -478,8 +482,7 @@ namespace BitTorrent bool addMoveTorrentStorageJob(TorrentImpl *torrent, const Path &newPath, MoveStorageMode mode, MoveStorageContext context); - void findIncompleteFiles(const TorrentInfo &torrentInfo, const Path &savePath - , const Path &downloadPath, const PathList &filePaths = {}) const; + QFuture findIncompleteFiles(const Path &savePath, const Path &downloadPath, const PathList &filePaths = {}) const; void enablePortMapping(); void disablePortMapping(); @@ -514,7 +517,6 @@ namespace BitTorrent void generateResumeData(); void handleIPFilterParsed(int ruleCount); void handleIPFilterError(); - void fileSearchFinished(const TorrentID &id, const Path &savePath, const PathList &fileNames); void torrentContentRemovingFinished(const QString &torrentName, const QString &errorMessage); private: diff --git a/src/base/bittorrent/torrent.h b/src/base/bittorrent/torrent.h index d2eefbabf..fc817b86c 100644 --- a/src/base/bittorrent/torrent.h +++ b/src/base/bittorrent/torrent.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015-2024 Vladimir Golovnev + * Copyright (C) 2015-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -45,6 +45,8 @@ class QByteArray; class QDateTime; class QUrl; +template class QFuture; + namespace BitTorrent { enum class DownloadPriority; @@ -275,10 +277,7 @@ namespace BitTorrent virtual bool isDHTDisabled() const = 0; virtual bool isPEXDisabled() const = 0; virtual bool isLSDDisabled() const = 0; - virtual QList peers() const = 0; virtual QBitArray pieces() const = 0; - virtual QBitArray downloadingPieces() const = 0; - virtual QList pieceAvailability() const = 0; virtual qreal distributedCopies() const = 0; virtual qreal maxRatio() const = 0; virtual int maxSeedingTime() const = 0; @@ -325,10 +324,10 @@ namespace BitTorrent virtual nonstd::expected exportToBuffer() const = 0; virtual nonstd::expected exportToFile(const Path &path) const = 0; - virtual void fetchPeerInfo(std::function)> resultHandler) const = 0; - virtual void fetchURLSeeds(std::function)> resultHandler) const = 0; - virtual void fetchPieceAvailability(std::function)> resultHandler) const = 0; - virtual void fetchDownloadingPieces(std::function resultHandler) const = 0; + virtual QFuture> fetchPeerInfo() const = 0; + virtual QFuture> fetchURLSeeds() const = 0; + virtual QFuture> fetchPieceAvailability() const = 0; + virtual QFuture fetchDownloadingPieces() const = 0; TorrentID id() const; bool isRunning() const; diff --git a/src/base/bittorrent/torrentcontenthandler.h b/src/base/bittorrent/torrentcontenthandler.h index 2e3702d2c..350db753e 100644 --- a/src/base/bittorrent/torrentcontenthandler.h +++ b/src/base/bittorrent/torrentcontenthandler.h @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2022-2023 Vladimir Golovnev + * Copyright (C) 2022-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -34,6 +34,8 @@ #include "abstractfilestorage.h" #include "downloadpriority.h" +template class QFuture; + namespace BitTorrent { class TorrentContentHandler : public QObject, public AbstractFileStorage @@ -52,8 +54,7 @@ namespace BitTorrent * This is not the same as torrrent availability, it is just a fraction of pieces * that can be downloaded right now. It varies between 0 to 1. */ - virtual QList availableFileFractions() const = 0; - virtual void fetchAvailableFileFractions(std::function)> resultHandler) const = 0; + virtual QFuture> fetchAvailableFileFractions() const = 0; virtual void prioritizeFiles(const QList &priorities) = 0; virtual void flushCache() const = 0; diff --git a/src/base/bittorrent/torrentimpl.cpp b/src/base/bittorrent/torrentimpl.cpp index 45cc7f3bf..a670db151 100644 --- a/src/base/bittorrent/torrentimpl.cpp +++ b/src/base/bittorrent/torrentimpl.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2015-2024 Vladimir Golovnev + * Copyright (C) 2015-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -51,7 +51,9 @@ #include #include #include +#include #include +#include #include #include #include @@ -67,6 +69,7 @@ #include "common.h" #include "downloadpriority.h" #include "extensiondata.h" +#include "filesearcher.h" #include "loadtorrentparams.h" #include "ltqbitarray.h" #include "lttypecast.h" @@ -1465,48 +1468,11 @@ bool TorrentImpl::isLSDDisabled() const return static_cast(m_nativeStatus.flags & lt::torrent_flags::disable_lsd); } -QList TorrentImpl::peers() const -{ - std::vector nativePeers; - m_nativeHandle.get_peer_info(nativePeers); - - QList peers; - peers.reserve(static_cast(nativePeers.size())); - - for (const lt::peer_info &peer : nativePeers) - peers.append(PeerInfo(peer, pieces())); - - return peers; -} - QBitArray TorrentImpl::pieces() const { return m_pieces; } -QBitArray TorrentImpl::downloadingPieces() const -{ - if (!hasMetadata()) - return {}; - - std::vector queue; - m_nativeHandle.get_download_queue(queue); - - QBitArray result {piecesCount()}; - for (const lt::partial_piece_info &info : queue) - result.setBit(LT::toUnderlyingType(info.piece_index)); - - return result; -} - -QList TorrentImpl::pieceAvailability() const -{ - std::vector avail; - m_nativeHandle.piece_availability(avail); - - return {avail.cbegin(), avail.cend()}; -} - qreal TorrentImpl::distributedCopies() const { return m_nativeStatus.distributed_copies; @@ -1751,12 +1717,6 @@ void TorrentImpl::applyFirstLastPiecePriority(const bool enabled) m_nativeHandle.prioritize_pieces(piecePriorities); } -void TorrentImpl::fileSearchFinished(const Path &savePath, const PathList &fileNames) -{ - if (m_maintenanceJob == MaintenanceJob::HandleMetadata) - endReceivedMetadataHandling(savePath, fileNames); -} - TrackerEntryStatus TorrentImpl::updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash> &updateInfo) { const auto it = std::find_if(m_trackerEntryStatuses.begin(), m_trackerEntryStatuses.end() @@ -2150,7 +2110,7 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p) // URL seed list have been changed by libtorrent for some reason, so we need to update cached one. // Unfortunately, URL seed list containing in "resume data" is generated according to different rules // than the list we usually cache, so we have to request it from the appropriate source. - fetchURLSeeds([this](const QList &urlSeeds) { m_urlSeeds = urlSeeds; }); + fetchURLSeeds().then(this, [this](const QList &urlSeeds) { m_urlSeeds = urlSeeds; }); } if ((m_maintenanceJob == MaintenanceJob::HandleMetadata) && p->params.ti) @@ -2197,7 +2157,12 @@ void TorrentImpl::handleSaveResumeDataAlert(const lt::save_resume_data_alert *p) filePaths[i] = Path(it->second); } - m_session->findIncompleteFiles(metadata, savePath(), downloadPath(), filePaths); + m_session->findIncompleteFiles(savePath(), downloadPath(), filePaths).then(this + , [this](const FileSearchResult &result) + { + if (m_maintenanceJob == MaintenanceJob::HandleMetadata) + endReceivedMetadataHandling(result.savePath, result.fileNames); + }); } else { @@ -2930,9 +2895,9 @@ nonstd::expected TorrentImpl::exportToFile(const Path &path) cons return {}; } -void TorrentImpl::fetchPeerInfo(std::function)> resultHandler) const +QFuture> TorrentImpl::fetchPeerInfo() const { - invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QList + return invokeAsync([nativeHandle = m_nativeHandle, allPieces = pieces()]() -> QList { try { @@ -2947,13 +2912,12 @@ void TorrentImpl::fetchPeerInfo(std::function)> resultHand catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } -void TorrentImpl::fetchURLSeeds(std::function)> resultHandler) const +QFuture> TorrentImpl::fetchURLSeeds() const { - invokeAsync([nativeHandle = m_nativeHandle]() -> QList + return invokeAsync([nativeHandle = m_nativeHandle]() -> QList { try { @@ -2967,13 +2931,12 @@ void TorrentImpl::fetchURLSeeds(std::function)> resultHandler) catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } -void TorrentImpl::fetchPieceAvailability(std::function)> resultHandler) const +QFuture> TorrentImpl::fetchPieceAvailability() const { - invokeAsync([nativeHandle = m_nativeHandle]() -> QList + return invokeAsync([nativeHandle = m_nativeHandle]() -> QList { try { @@ -2984,13 +2947,12 @@ void TorrentImpl::fetchPieceAvailability(std::function)> result catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } -void TorrentImpl::fetchDownloadingPieces(std::function resultHandler) const +QFuture TorrentImpl::fetchDownloadingPieces() const { - invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QBitArray + return invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QBitArray { try { @@ -3009,13 +2971,12 @@ void TorrentImpl::fetchDownloadingPieces(std::function resultH catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } -void TorrentImpl::fetchAvailableFileFractions(std::function)> resultHandler) const +QFuture> TorrentImpl::fetchAvailableFileFractions() const { - invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QList + return invokeAsync([nativeHandle = m_nativeHandle, torrentInfo = m_torrentInfo]() -> QList { if (!torrentInfo.isValid() || (torrentInfo.filesCount() <= 0)) return {}; @@ -3049,8 +3010,7 @@ void TorrentImpl::fetchAvailableFileFractions(std::function)> catch (const std::exception &) {} return {}; - } - , std::move(resultHandler)); + }); } void TorrentImpl::prioritizeFiles(const QList &priorities) @@ -3090,47 +3050,17 @@ void TorrentImpl::prioritizeFiles(const QList &priorities) manageActualFilePaths(); } -QList TorrentImpl::availableFileFractions() const +template +QFuture> TorrentImpl::invokeAsync(Func &&func) const { - Q_ASSERT(hasMetadata()); - - const int filesCount = this->filesCount(); - if (filesCount <= 0) return {}; - - const QList piecesAvailability = pieceAvailability(); - // libtorrent returns empty array for seeding only torrents - if (piecesAvailability.empty()) return QList(filesCount, -1); - - QList res; - res.reserve(filesCount); - for (int i = 0; i < filesCount; ++i) + QPromise> promise; + const auto future = promise.future(); + m_session->invokeAsync([func = std::forward(func), promise = std::move(promise)]() mutable { - const TorrentInfo::PieceRange filePieces = m_torrentInfo.filePieces(i); - - int availablePieces = 0; - for (const int piece : filePieces) - availablePieces += (piecesAvailability[piece] > 0) ? 1 : 0; - - const qreal availability = filePieces.isEmpty() - ? 1 // the file has no pieces, so it is available by default - : static_cast(availablePieces) / filePieces.size(); - res.push_back(availability); - } - return res; -} - -template -void TorrentImpl::invokeAsync(Func func, Callback resultHandler) const -{ - m_session->invokeAsync([session = m_session - , func = std::move(func) - , resultHandler = std::move(resultHandler) - , thisTorrent = QPointer(this)]() mutable - { - session->invoke([result = func(), thisTorrent, resultHandler = std::move(resultHandler)] - { - if (thisTorrent) - resultHandler(result); - }); + promise.start(); + promise.addResult(func()); + promise.finish(); }); + + return future; } diff --git a/src/base/bittorrent/torrentimpl.h b/src/base/bittorrent/torrentimpl.h index 5c54b2d00..87137cd73 100644 --- a/src/base/bittorrent/torrentimpl.h +++ b/src/base/bittorrent/torrentimpl.h @@ -203,10 +203,7 @@ namespace BitTorrent bool isDHTDisabled() const override; bool isPEXDisabled() const override; bool isLSDDisabled() const override; - QList peers() const override; QBitArray pieces() const override; - QBitArray downloadingPieces() const override; - QList pieceAvailability() const override; qreal distributedCopies() const override; qreal maxRatio() const override; int maxSeedingTime() const override; @@ -220,7 +217,6 @@ namespace BitTorrent int connectionsCount() const override; int connectionsLimit() const override; qlonglong nextAnnounce() const override; - QList availableFileFractions() const override; void setName(const QString &name) override; void setSequentialDownload(bool enable) override; @@ -258,11 +254,11 @@ namespace BitTorrent nonstd::expected exportToBuffer() const override; nonstd::expected exportToFile(const Path &path) const override; - void fetchPeerInfo(std::function)> resultHandler) const override; - void fetchURLSeeds(std::function)> resultHandler) const override; - void fetchPieceAvailability(std::function)> resultHandler) const override; - void fetchDownloadingPieces(std::function resultHandler) const override; - void fetchAvailableFileFractions(std::function)> resultHandler) const override; + QFuture> fetchPeerInfo() const override; + QFuture> fetchURLSeeds() const override; + QFuture> fetchPieceAvailability() const override; + QFuture fetchDownloadingPieces() const override; + QFuture> fetchAvailableFileFractions() const override; bool needSaveResumeData() const; @@ -278,7 +274,6 @@ namespace BitTorrent void requestResumeData(lt::resume_data_flags_t flags = {}); void deferredRequestResumeData(); void handleMoveStorageJobFinished(const Path &path, MoveStorageContext context, bool hasOutstandingJob); - void fileSearchFinished(const Path &savePath, const PathList &fileNames); TrackerEntryStatus updateTrackerEntryStatus(const lt::announce_entry &announceEntry, const QHash> &updateInfo); void resetTrackerEntryStatuses(); @@ -326,8 +321,8 @@ namespace BitTorrent nonstd::expected exportTorrent() const; - template - void invokeAsync(Func func, Callback resultHandler) const; + template + QFuture> invokeAsync(Func &&func) const; SessionImpl *const m_session = nullptr; lt::session *m_nativeSession = nullptr; diff --git a/src/gui/addnewtorrentdialog.cpp b/src/gui/addnewtorrentdialog.cpp index 321e1528b..8954a1c4d 100644 --- a/src/gui/addnewtorrentdialog.cpp +++ b/src/gui/addnewtorrentdialog.cpp @@ -32,12 +32,14 @@ #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -242,14 +244,13 @@ public: return QList(filesCount(), 0); } - QList availableFileFractions() const override + QFuture> fetchAvailableFileFractions() const override { - return QList(filesCount(), 0); - } - - void fetchAvailableFileFractions(std::function)> resultHandler) const override - { - resultHandler(availableFileFractions()); +#if (QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)) + return QtFuture::makeReadyValueFuture(QList(filesCount(), 0)); +#else + return QtFuture::makeReadyFuture(QList(filesCount(), 0)); +#endif } void prioritizeFiles(const QList &priorities) override diff --git a/src/gui/properties/peerlistwidget.cpp b/src/gui/properties/peerlistwidget.cpp index 8546d7143..a6c44d106 100644 --- a/src/gui/properties/peerlistwidget.cpp +++ b/src/gui/properties/peerlistwidget.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2023 Vladimir Golovnev + * Copyright (C) 2023-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -406,7 +407,7 @@ void PeerListWidget::loadPeers(const BitTorrent::Torrent *torrent) return; using TorrentPtr = QPointer; - torrent->fetchPeerInfo([this, torrent = TorrentPtr(torrent)](const QList &peers) + torrent->fetchPeerInfo().then(this, [this, torrent = TorrentPtr(torrent)](const QList &peers) { if (torrent != m_properties->getCurrentTorrent()) return; diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index ad9673272..303043a80 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2022-2024 Vladimir Golovnev + * Copyright (C) 2022-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -471,15 +472,15 @@ void PropertiesWidget::loadDynamicData() if (m_torrent->hasMetadata()) { - using TorrentPtr = QPointer; - m_ui->labelTotalPiecesVal->setText(tr("%1 x %2 (have %3)", "(torrent pieces) eg 152 x 4MB (have 25)").arg(m_torrent->piecesCount()).arg(Utils::Misc::friendlyUnit(m_torrent->pieceLength())).arg(m_torrent->piecesHave())); if (!m_torrent->isFinished() && !m_torrent->isStopped() && !m_torrent->isQueued() && !m_torrent->isChecking()) { // Pieces availability showPiecesAvailability(true); - m_torrent->fetchPieceAvailability([this, torrent = TorrentPtr(m_torrent)](const QList &pieceAvailability) + + using TorrentPtr = QPointer; + m_torrent->fetchPieceAvailability().then(this, [this, torrent = TorrentPtr(m_torrent)](const QList &pieceAvailability) { if (torrent == m_torrent) m_piecesAvailability->setAvailability(pieceAvailability); @@ -496,10 +497,9 @@ void PropertiesWidget::loadDynamicData() qreal progress = m_torrent->progress() * 100.; m_ui->labelProgressVal->setText(Utils::String::fromDouble(progress, 1) + u'%'); - m_torrent->fetchDownloadingPieces([this, torrent = TorrentPtr(m_torrent)](const QBitArray &downloadingPieces) + m_torrent->fetchDownloadingPieces().then(this, [this](const QBitArray &downloadingPieces) { - if (torrent == m_torrent) - m_downloadedPieces->setProgress(m_torrent->pieces(), downloadingPieces); + m_downloadedPieces->setProgress(m_torrent->pieces(), downloadingPieces); }); } else @@ -525,7 +525,7 @@ void PropertiesWidget::loadUrlSeeds() return; using TorrentPtr = QPointer; - m_torrent->fetchURLSeeds([this, torrent = TorrentPtr(m_torrent)](const QList &urlSeeds) + m_torrent->fetchURLSeeds().then(this, [this, torrent = TorrentPtr(m_torrent)](const QList &urlSeeds) { if (torrent != m_torrent) return; diff --git a/src/gui/torrentcontentmodel.cpp b/src/gui/torrentcontentmodel.cpp index e75fc1856..32e44c16f 100644 --- a/src/gui/torrentcontentmodel.cpp +++ b/src/gui/torrentcontentmodel.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2022-2024 Vladimir Golovnev + * Copyright (C) 2022-2025 Vladimir Golovnev * Copyright (C) 2006-2012 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -33,6 +33,7 @@ #include #include +#include #include #include #include @@ -219,7 +220,8 @@ void TorrentContentModel::updateFilesAvailability() Q_ASSERT(m_contentHandler && m_contentHandler->hasMetadata()); using HandlerPtr = QPointer; - m_contentHandler->fetchAvailableFileFractions([this, handler = HandlerPtr(m_contentHandler)](const QList &availableFileFractions) + m_contentHandler->fetchAvailableFileFractions().then(this + , [this, handler = HandlerPtr(m_contentHandler)](const QList &availableFileFractions) { if (handler != m_contentHandler) return; diff --git a/src/gui/trackerlist/trackerlistmodel.cpp b/src/gui/trackerlist/trackerlistmodel.cpp index 78465488c..b13b067b6 100644 --- a/src/gui/trackerlist/trackerlistmodel.cpp +++ b/src/gui/trackerlist/trackerlistmodel.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2023-2024 Vladimir Golovnev + * Copyright (C) 2023-2025 Vladimir Golovnev * Copyright (C) 2006 Christophe Dumez * * This program is free software; you can redistribute it and/or @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -309,7 +310,7 @@ void TrackerListModel::populate() m_items->emplace_back(std::make_shared(u"** [LSD] **", privateTorrentMessage)); using TorrentPtr = QPointer; - m_torrent->fetchPeerInfo([this, torrent = TorrentPtr(m_torrent)](const QList &peers) + m_torrent->fetchPeerInfo().then(this, [this, torrent = TorrentPtr(m_torrent)](const QList &peers) { if (torrent != m_torrent) return; diff --git a/src/webui/api/synccontroller.cpp b/src/webui/api/synccontroller.cpp index 222b066aa..574a833ed 100644 --- a/src/webui/api/synccontroller.cpp +++ b/src/webui/api/synccontroller.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2018-2024 Vladimir Golovnev + * Copyright (C) 2018-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -28,6 +28,7 @@ #include "synccontroller.h" +#include #include #include #include @@ -745,7 +746,7 @@ void SyncController::torrentPeersAction() QVariantMap data; QVariantHash peers; - const QList peersList = torrent->peers(); + const QList peersList = torrent->fetchPeerInfo().takeResult(); bool resolvePeerCountries = Preferences::instance()->resolvePeerCountries(); diff --git a/src/webui/api/torrentscontroller.cpp b/src/webui/api/torrentscontroller.cpp index 6fd5f6fbf..c877fd5db 100644 --- a/src/webui/api/torrentscontroller.cpp +++ b/src/webui/api/torrentscontroller.cpp @@ -1,6 +1,6 @@ /* * Bittorrent Client using Qt and libtorrent. - * Copyright (C) 2018-2023 Vladimir Golovnev + * Copyright (C) 2018-2025 Vladimir Golovnev * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -32,6 +32,7 @@ #include #include +#include #include #include #include @@ -180,9 +181,11 @@ namespace QJsonArray getStickyTrackers(const BitTorrent::Torrent *const torrent) { int seedsDHT = 0, seedsPeX = 0, seedsLSD = 0, leechesDHT = 0, leechesPeX = 0, leechesLSD = 0; - for (const BitTorrent::PeerInfo &peer : asConst(torrent->peers())) + const QList peersList = torrent->fetchPeerInfo().takeResult(); + for (const BitTorrent::PeerInfo &peer : peersList) { - if (peer.isConnecting()) continue; + if (peer.isConnecting()) + continue; if (peer.isSeed()) { @@ -727,7 +730,7 @@ void TorrentsController::filesAction() { const QList priorities = torrent->filePriorities(); const QList fp = torrent->filesProgress(); - const QList fileAvailability = torrent->availableFileFractions(); + const QList fileAvailability = torrent->fetchAvailableFileFractions().takeResult(); const BitTorrent::TorrentInfo info = torrent->info(); for (const int index : asConst(fileIndexes)) { @@ -796,7 +799,7 @@ void TorrentsController::pieceStatesAction() for (int i = 0; i < states.size(); ++i) pieceStates.append(static_cast(states[i]) * 2); - const QBitArray dlstates = torrent->downloadingPieces(); + const QBitArray dlstates = torrent->fetchDownloadingPieces().takeResult(); for (int i = 0; i < states.size(); ++i) { if (dlstates[i])