From d8401c76f568e463022dd653124e8c99f33d0020 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Sat, 4 Apr 2020 12:50:11 +0800 Subject: [PATCH 1/4] Avoid holding encoded resume data in memory Now it the encoded resume data will be streamed to file instead of a temporary buffer holding the whole of it. --- .../private/resumedatasavingmanager.cpp | 22 +++++++++++++++++++ .../private/resumedatasavingmanager.h | 7 +++++- src/base/bittorrent/session.cpp | 15 +++++-------- src/base/bittorrent/session.h | 3 ++- src/base/bittorrent/torrenthandle.cpp | 15 ++++++++----- 5 files changed, 44 insertions(+), 18 deletions(-) diff --git a/src/base/bittorrent/private/resumedatasavingmanager.cpp b/src/base/bittorrent/private/resumedatasavingmanager.cpp index 3f23376a3..17784c5ad 100644 --- a/src/base/bittorrent/private/resumedatasavingmanager.cpp +++ b/src/base/bittorrent/private/resumedatasavingmanager.cpp @@ -28,11 +28,15 @@ #include "resumedatasavingmanager.h" +#include +#include + #include #include #include "base/logger.h" #include "base/utils/fs.h" +#include "base/utils/io.h" ResumeDataSavingManager::ResumeDataSavingManager(const QString &resumeFolderPath) : m_resumeDataDir(resumeFolderPath) @@ -50,6 +54,24 @@ void ResumeDataSavingManager::save(const QString &filename, const QByteArray &da } } +void ResumeDataSavingManager::save(const QString &filename, const std::shared_ptr &data) const +{ + const QString filepath = m_resumeDataDir.absoluteFilePath(filename); + + QSaveFile file {filepath}; + if (!file.open(QIODevice::WriteOnly)) { + LogMsg(tr("Couldn't save data to '%1'. Error: %2") + .arg(filepath, file.errorString()), Log::CRITICAL); + return; + } + + lt::bencode(Utils::IO::FileDeviceOutputIterator {file}, *data); + if ((file.error() != QFileDevice::NoError) || !file.commit()) { + LogMsg(tr("Couldn't save data to '%1'. Error: %2") + .arg(filepath, file.errorString()), Log::CRITICAL); + } +} + void ResumeDataSavingManager::remove(const QString &filename) const { const QString filepath = m_resumeDataDir.absoluteFilePath(filename); diff --git a/src/base/bittorrent/private/resumedatasavingmanager.h b/src/base/bittorrent/private/resumedatasavingmanager.h index ffef21e91..47a9ca562 100644 --- a/src/base/bittorrent/private/resumedatasavingmanager.h +++ b/src/base/bittorrent/private/resumedatasavingmanager.h @@ -28,6 +28,10 @@ #pragma once +#include + +#include + #include #include @@ -43,8 +47,9 @@ public: public slots: void save(const QString &filename, const QByteArray &data) const; + void save(const QString &filename, const std::shared_ptr &data) const; void remove(const QString &filename) const; private: - QDir m_resumeDataDir; + const QDir m_resumeDataDir; }; diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index a932e74ea..f0d37f00c 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -3989,25 +3989,20 @@ void Session::handleTorrentFinished(TorrentHandle *const torrent) emit allTorrentsFinished(); } -void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const lt::entry &data) +void Session::handleTorrentResumeDataReady(TorrentHandle *const torrent, const std::shared_ptr &data) { --m_numResumeData; // Separated thread is used for the blocking IO which results in slow processing of many torrents. - // Encoding data in parallel while doing IO saves time. Copying lt::entry objects around - // isn't cheap too. - - QByteArray out; - out.reserve(1024 * 1024); // most fastresume file sizes are under 1 MB - lt::bencode(std::back_inserter(out), data); + // Copying lt::entry objects around isn't cheap. const QString filename = QString::fromLatin1("%1.fastresume").arg(torrent->hash()); #if (QT_VERSION >= QT_VERSION_CHECK(5, 10, 0)) QMetaObject::invokeMethod(m_resumeDataSavingManager - , [this, filename, out]() { m_resumeDataSavingManager->save(filename, out); }); + , [this, filename, data]() { m_resumeDataSavingManager->save(filename, data); }); #else - QMetaObject::invokeMethod(m_resumeDataSavingManager, "save", - Q_ARG(QString, filename), Q_ARG(QByteArray, out)); + QMetaObject::invokeMethod(m_resumeDataSavingManager, "save" + , Q_ARG(QString, filename), Q_ARG(std::shared_ptr, data)); #endif } diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 4f5bcc332..edb8024bc 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -30,6 +30,7 @@ #ifndef BITTORRENT_SESSION_H #define BITTORRENT_SESSION_H +#include #include #include @@ -459,7 +460,7 @@ namespace BitTorrent void handleTorrentTrackersChanged(TorrentHandle *const torrent); void handleTorrentUrlSeedsAdded(TorrentHandle *const torrent, const QVector &newUrlSeeds); void handleTorrentUrlSeedsRemoved(TorrentHandle *const torrent, const QVector &urlSeeds); - void handleTorrentResumeDataReady(TorrentHandle *const torrent, const lt::entry &data); + void handleTorrentResumeDataReady(TorrentHandle *const torrent, const std::shared_ptr &data); void handleTorrentResumeDataFailed(TorrentHandle *const torrent); void handleTorrentTrackerReply(TorrentHandle *const torrent, const QString &trackerUrl); void handleTorrentTrackerWarning(TorrentHandle *const torrent, const QString &trackerUrl); diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp index d2b0ae73b..5f37ecc4c 100644 --- a/src/base/bittorrent/torrenthandle.cpp +++ b/src/base/bittorrent/torrenthandle.cpp @@ -30,6 +30,7 @@ #include "torrenthandle.h" #include +#include #include #ifdef Q_OS_WIN @@ -1647,14 +1648,16 @@ void TorrentHandle::handleSaveResumeDataAlert(const lt::save_resume_data_alert * { #if (LIBTORRENT_VERSION_NUM < 10200) const bool useDummyResumeData = !(p && p->resume_data); - lt::entry dummyEntry; - - lt::entry &resumeData = useDummyResumeData ? dummyEntry : *(p->resume_data); + auto resumeDataPtr = std::make_shared(useDummyResumeData + ? lt::entry {} + : *(p->resume_data)); #else const bool useDummyResumeData = !p; - - lt::entry resumeData = useDummyResumeData ? lt::entry() : lt::write_resume_data(p->params); + auto resumeDataPtr = std::make_shared(useDummyResumeData + ? lt::entry {} + : lt::write_resume_data(p->params)); #endif + lt::entry &resumeData = *resumeDataPtr; updateStatus(); @@ -1697,7 +1700,7 @@ void TorrentHandle::handleSaveResumeDataAlert(const lt::save_resume_data_alert * resumeData["auto_managed"] = false; } - m_session->handleTorrentResumeDataReady(this, resumeData); + m_session->handleTorrentResumeDataReady(this, resumeDataPtr); } void TorrentHandle::handleSaveResumeDataFailedAlert(const lt::save_resume_data_failed_alert *p) From f76d56e22463e546c2151106dd1b74cbf302c0ab Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Mon, 6 Apr 2020 15:29:55 +0800 Subject: [PATCH 2/4] Suppress unused variable warning on macOS --- src/gui/mainwindow.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index a12ede08a..55bcc1440 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -118,8 +118,10 @@ namespace // Misc const QString KEY_DOWNLOAD_TRACKER_FAVICON = QStringLiteral(SETTINGS_KEY("DownloadTrackerFavicon")); - const int TIME_TRAY_BALLOON = 5000; const std::chrono::seconds PREVENT_SUSPEND_INTERVAL {60}; +#if !defined(Q_OS_MACOS) + const int TIME_TRAY_BALLOON = 5000; +#endif // just a shortcut inline SettingsStorage *settings() From d476ae4f14df879863590ea1c6237557d6a4f0f7 Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 8 Apr 2020 11:58:12 +0800 Subject: [PATCH 3/4] Fix header inclusion order --- src/base/bittorrent/session.cpp | 38 ++++++++++++++++----------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index f0d37f00c..eaa4e6e3f 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -40,25 +40,6 @@ #include #endif -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef Q_OS_WIN -// TODO: Remove together with fixBrokenSavePath() -#define NEED_TO_FIX_BROKEN_PATH -#include -#endif - #include #include #include @@ -78,6 +59,25 @@ #include #endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef Q_OS_WIN +// TODO: Remove together with fixBrokenSavePath() +#define NEED_TO_FIX_BROKEN_PATH +#include +#endif + #include "base/algorithm.h" #include "base/exceptions.h" #include "base/global.h" From e7890fb727b16fa19c3863dace0a71a14d20c17e Mon Sep 17 00:00:00 2001 From: Chocobo1 Date: Wed, 8 Apr 2020 12:45:03 +0800 Subject: [PATCH 4/4] Preallocate output buffer --- src/base/bittorrent/session.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index eaa4e6e3f..55a2052a6 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -2587,6 +2587,7 @@ void Session::saveResumeData() void Session::saveTorrentsQueue() { + // store hash in textual representation QMap queue; // Use QMap since it should be ordered by key for (const TorrentHandle *torrent : asConst(torrents())) { // We require actual (non-cached) queue position here! @@ -2596,6 +2597,7 @@ void Session::saveTorrentsQueue() } QByteArray data; + data.reserve(((InfoHash::length() * 2) + 1) * queue.size()); for (const QString &hash : asConst(queue)) data += (hash.toLatin1() + '\n');