From 9399b876eb832eaef7a8581dda2d2ac88d7ec67d Mon Sep 17 00:00:00 2001 From: "Vladimir Golovnev (Glassez)" Date: Fri, 28 Jun 2019 21:24:39 +0300 Subject: [PATCH] Fix torrent checking issues Start all torrents auto-managed to prevent simultaneous checking of multiple torrents. Handle checking state of paused torrent to prevent it from being resumed when qBittorrent is closed until checking isn't complete. --- src/base/bittorrent/session.cpp | 106 +++++++++++++++++--------- src/base/bittorrent/session.h | 1 + src/base/bittorrent/torrenthandle.cpp | 74 +++++++++++------- src/base/bittorrent/torrenthandle.h | 11 ++- 4 files changed, 124 insertions(+), 68 deletions(-) diff --git a/src/base/bittorrent/session.cpp b/src/base/bittorrent/session.cpp index 4da1db24d..f3d245b1a 100644 --- a/src/base/bittorrent/session.cpp +++ b/src/base/bittorrent/session.cpp @@ -1861,29 +1861,22 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne hash = magnetUri.hash(); if (m_loadedMetadata.contains(hash)) { - // Adding preloaded torrent - m_loadedMetadata.remove(hash); - lt::torrent_handle handle = m_nativeSession->find_torrent(hash); - --m_extraLimit; + // Adding preloaded torrent... + m_addingTorrents.insert(hash, params); + lt::torrent_handle handle = m_nativeSession->find_torrent(hash); + // We need to pause it first to create TorrentHandle within the same + // underlying state as in other cases. try { - // Preloaded torrent is in "Upload mode" so we need to disable it - // otherwise the torrent never be downloaded (until application restart) #if (LIBTORRENT_VERSION_NUM < 10200) handle.auto_managed(false); - handle.set_upload_mode(false); #else - handle.unset_flags(lt::torrent_flags::auto_managed | lt::torrent_flags::upload_mode); + handle.unset_flags(lt::torrent_flags::auto_managed); #endif handle.pause(); } catch (const std::exception &) {} - adjustLimits(); - - // use common 2nd step of torrent addition - m_addingTorrents.insert(hash, params); - createTorrentHandle(handle); return true; } @@ -1923,8 +1916,15 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne if (!fromMagnetUri) { if (params.restored) { // load from existing fastresume #if (LIBTORRENT_VERSION_NUM < 10200) - p.resume_data = std::vector {fastresumeData.constData() - , (fastresumeData.constData() + fastresumeData.size())}; + // Make sure the torrent will be initially checked and then paused + // to perform some service jobs on it. We will start it if needed. + // (Workaround to easily support libtorrent-1.1 + QByteArray patchedFastresumeData = fastresumeData; + patchedFastresumeData.replace("6:pausedi0e", "6:pausedi1e"); + patchedFastresumeData.replace("12:auto_managedi0e", "12:auto_managedi1e"); + + p.resume_data = std::vector {patchedFastresumeData.constData() + , (patchedFastresumeData.constData() + patchedFastresumeData.size())}; p.flags |= lt::add_torrent_params::flag_use_resume_save_path; // Still setup the default parameters and let libtorrent handle @@ -1975,18 +1975,25 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne p.ti = torrentInfo.nativeInfo(); } - if (!hasCompleteFastresume) { - // Common + // Common #if (LIBTORRENT_VERSION_NUM < 10200) - p.flags |= lt::add_torrent_params::flag_paused; // Start in pause - p.flags &= ~lt::add_torrent_params::flag_auto_managed; // Because it is added in paused state - p.flags &= ~lt::add_torrent_params::flag_duplicate_is_error; // Already checked + p.flags &= ~lt::add_torrent_params::flag_duplicate_is_error; // Already checked #else - p.flags |= lt::torrent_flags::paused; // Start in pause - p.flags &= ~lt::torrent_flags::auto_managed; // Because it is added in paused state - p.flags &= ~lt::torrent_flags::duplicate_is_error; // Already checked + p.flags &= ~lt::torrent_flags::duplicate_is_error; // Already checked +#endif + // Make sure the torrent will be initially checked and then paused + // to perform some service jobs on it. We will start it if needed. +#if (LIBTORRENT_VERSION_NUM < 10200) + p.flags |= lt::add_torrent_params::flag_paused; + p.flags |= lt::add_torrent_params::flag_auto_managed; + p.flags |= lt::add_torrent_params::flag_stop_when_ready; +#else + p.flags |= lt::torrent_flags::paused; + p.flags |= lt::torrent_flags::auto_managed; + p.flags |= lt::torrent_flags::stop_when_ready; #endif + if (!hasCompleteFastresume) { // Limits p.max_connections = maxConnectionsPerTorrent(); p.max_uploads = maxUploadsPerTorrent(); @@ -2012,16 +2019,6 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne p.flags &= ~lt::add_torrent_params::flag_seed_mode; #else p.flags &= ~lt::torrent_flags::seed_mode; -#endif - } - - if (params.restored && !params.paused) { - // Make sure the torrent will restored in "paused" state - // Then we will start it if needed -#if (LIBTORRENT_VERSION_NUM < 10200) - p.flags |= lt::add_torrent_params::flag_stop_when_ready; -#else - p.flags |= lt::torrent_flags::stop_when_ready; #endif } } @@ -2105,6 +2102,7 @@ bool Session::loadMetadata(const MagnetUri &magnetUri) p.flags &= ~lt::torrent_flags::paused; p.flags &= ~lt::torrent_flags::auto_managed; #endif + // Solution to avoid accidental file writes #if (LIBTORRENT_VERSION_NUM < 10200) p.flags |= lt::add_torrent_params::flag_upload_mode; @@ -3872,10 +3870,7 @@ void Session::handleAlert(const lt::alert *a) case lt::tracker_warning_alert::alert_type: case lt::fastresume_rejected_alert::alert_type: case lt::torrent_checked_alert::alert_type: - dispatchTorrentAlert(a); - break; case lt::metadata_received_alert::alert_type: - handleMetadataReceivedAlert(static_cast(a)); dispatchTorrentAlert(a); break; case lt::state_update_alert::alert_type: @@ -3933,8 +3928,19 @@ void Session::handleAlert(const lt::alert *a) void Session::dispatchTorrentAlert(const lt::alert *a) { TorrentHandle *const torrent = m_torrents.value(static_cast(a)->handle.info_hash()); - if (torrent) + if (torrent) { torrent->handleAlert(a); + return; + } + + switch (a->type()) { + case lt::torrent_paused_alert::alert_type: + handleTorrentPausedAlert(static_cast(a)); + break; + case lt::metadata_received_alert::alert_type: + handleMetadataReceivedAlert(static_cast(a)); + break; + } } void Session::createTorrentHandle(const lt::torrent_handle &nativeHandle) @@ -4064,6 +4070,32 @@ void Session::handleMetadataReceivedAlert(const lt::metadata_received_alert *p) } } +void Session::handleTorrentPausedAlert(const libtorrent::torrent_paused_alert *p) +{ + const InfoHash hash {p->handle.info_hash()}; + + if (m_loadedMetadata.contains(hash)) { + // Adding preloaded torrent + m_loadedMetadata.remove(hash); + lt::torrent_handle handle = p->handle; + --m_extraLimit; + + // Preloaded torrent is in "Upload mode" so we need to disable it + // otherwise the torrent never be downloaded (until application restart) +#if (LIBTORRENT_VERSION_NUM < 10200) + handle.set_upload_mode(false); + handle.auto_managed(true); +#else + handle.unset_flags(lt::torrent_flags::upload_mode); + handle.set_flags(lt::torrent_flags::auto_managed); +#endif + + adjustLimits(); + // use common 2nd step of torrent addition + createTorrentHandle(handle); + } +} + void Session::handleFileErrorAlert(const lt::file_error_alert *p) { TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash()); diff --git a/src/base/bittorrent/session.h b/src/base/bittorrent/session.h index 1474a6161..ec6fe397e 100644 --- a/src/base/bittorrent/session.h +++ b/src/base/bittorrent/session.h @@ -551,6 +551,7 @@ namespace BitTorrent void handleAddTorrentAlert(const lt::add_torrent_alert *p); void handleStateUpdateAlert(const lt::state_update_alert *p); void handleMetadataReceivedAlert(const lt::metadata_received_alert *p); + void handleTorrentPausedAlert(const lt::torrent_paused_alert *p); void handleFileErrorAlert(const lt::file_error_alert *p); void handleTorrentRemovedAlert(const lt::torrent_removed_alert *p); void handleTorrentDeletedAlert(const lt::torrent_deleted_alert *p); diff --git a/src/base/bittorrent/torrenthandle.cpp b/src/base/bittorrent/torrenthandle.cpp index c97e2ce7d..061e0a60c 100644 --- a/src/base/bittorrent/torrenthandle.cpp +++ b/src/base/bittorrent/torrenthandle.cpp @@ -191,6 +191,7 @@ TorrentHandle::TorrentHandle(Session *session, const lt::torrent_handle &nativeH , m_hasRootFolder(params.hasRootFolder) , m_needsToSetFirstLastPiecePriority(false) , m_needsToStartForced(params.forced) + , m_pauseWhenReady(params.paused) { if (m_useAutoTMM) m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category)); @@ -216,18 +217,18 @@ TorrentHandle::TorrentHandle(Session *session, const lt::torrent_handle &nativeH m_hasRootFolder = false; } - // "started" means "all initialization has completed and torrent has started regular processing". - // When torrent added/restored in "paused" state it become "started" immediately after construction. - // When it is added/restored in "resumed" state, it become "started" after it is really resumed - // (i.e. after receiving "torrent resumed" alert). - if (params.paused) { - m_startupState = Started; - } - else if (!params.restored || !hasMetadata()) { - // Resume torrent because it was added in "resumed" state - // but it's actually paused during initialization - m_startupState = Starting; - resume(params.forced); + if (!hasMetadata()) { + // There is nothing to prepare + if (!m_pauseWhenReady) { + // Resume torrent because it was added in "resumed" state + // but it's actually paused during initialization. + m_startupState = Starting; + resume(m_needsToStartForced); + } + else { + m_startupState = Started; + m_pauseWhenReady = false; + } } } @@ -1346,7 +1347,8 @@ void TorrentHandle::forceRecheck() #else m_nativeHandle.set_flags(lt::torrent_flags::stop_when_ready); #endif - resume_impl(false); + setAutoManaged(true); + m_pauseWhenReady = true; } } @@ -1618,17 +1620,22 @@ void TorrentHandle::handleTorrentCheckedAlert(const lt::torrent_checked_alert *p Q_UNUSED(p); qDebug("\"%s\" have just finished checking", qUtf8Printable(name())); - if (m_startupState == NotStarted) { - if (!m_hasMissingFiles) { - // Resume torrent because it was added in "resumed" state - // but it's actually paused during initialization. - m_startupState = Starting; - resume(m_needsToStartForced); + if (m_startupState == Preparing) { + if (!m_pauseWhenReady) { + if (!m_hasMissingFiles) { + // Resume torrent because it was added in "resumed" state + // but it's actually paused during initialization. + m_startupState = Starting; + resume(m_needsToStartForced); + } + else { + // Torrent that has missing files is paused. + m_startupState = Started; + } } else { - // Torrent that has missing files is marked as "started" - // but it remains paused. m_startupState = Started; + m_pauseWhenReady = false; } } @@ -1652,10 +1659,10 @@ void TorrentHandle::handleTorrentFinishedAlert(const lt::torrent_finished_alert Q_UNUSED(p); qDebug("Got a torrent finished alert for \"%s\"", qUtf8Printable(name())); qDebug("Torrent has seed status: %s", m_hasSeedStatus ? "yes" : "no"); + m_hasMissingFiles = false; if (m_hasSeedStatus) return; updateStatus(); - m_hasMissingFiles = false; m_hasSeedStatus = true; adjustActualSavePath(); @@ -1679,9 +1686,14 @@ void TorrentHandle::handleTorrentPausedAlert(const lt::torrent_paused_alert *p) Q_UNUSED(p); if (m_startupState == Started) { - updateStatus(); - m_speedMonitor.reset(); - m_session->handleTorrentPaused(this); + if (!m_pauseWhenReady) { + updateStatus(); + m_speedMonitor.reset(); + m_session->handleTorrentPaused(this); + } + else { + m_pauseWhenReady = false; + } } } @@ -1710,8 +1722,8 @@ void TorrentHandle::handleSaveResumeDataAlert(const lt::save_resume_data_alert * if (useDummyResumeData) { resumeData["qBt-magnetUri"] = toMagnetUri().toStdString(); - resumeData["qBt-paused"] = isPaused(); - resumeData["qBt-forced"] = isForced(); + resumeData["paused"] = isPaused(); + resumeData["auto_managed"] = isAutoManaged(); // Both firstLastPiecePriority and sequential need to be stored in the // resume data if there is no metadata, otherwise they won't be // restored if qBittorrent quits before the metadata are retrieved: @@ -1733,6 +1745,14 @@ void TorrentHandle::handleSaveResumeDataAlert(const lt::save_resume_data_alert * resumeData["qBt-queuePosition"] = (static_cast(nativeHandle().queue_position()) + 1); // qBt starts queue at 1 resumeData["qBt-hasRootFolder"] = m_hasRootFolder; + if (m_pauseWhenReady) { + // We need to redefine these values when torrent starting/rechecking + // in "paused" state since native values can be logically wrong + // (torrent can be not paused and auto_managed when it is checking). + resumeData["paused"] = true; + resumeData["auto_managed"] = false; + } + m_session->handleTorrentResumeDataReady(this, resumeData); } diff --git a/src/base/bittorrent/torrenthandle.h b/src/base/bittorrent/torrenthandle.h index 7471957c3..b64ea14be 100644 --- a/src/base/bittorrent/torrenthandle.h +++ b/src/base/bittorrent/torrenthandle.h @@ -447,12 +447,15 @@ namespace BitTorrent enum StartupState { - NotStarted, - Starting, - Started + Preparing, // torrent is preparing to start regular processing + Starting, // torrent is prepared and starting to perform regular processing + Started // torrent is performing regular processing }; + StartupState m_startupState = Preparing; + // Handle torrent state when it starts performing some service job + // being in Paused state so it might be unpaused internally and then paused again + bool m_pauseWhenReady; - StartupState m_startupState = NotStarted; bool m_unchecked = false; }; }