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.
This commit is contained in:
Vladimir Golovnev (Glassez) 2019-06-28 21:24:39 +03:00
commit a466ff5057
No known key found for this signature in database
GPG key ID: 52A2C7DEE2DFA6F7
4 changed files with 110 additions and 58 deletions

View file

@ -2148,25 +2148,19 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
hash = magnetUri.hash(); hash = magnetUri.hash();
if (m_loadedMetadata.contains(hash)) { if (m_loadedMetadata.contains(hash)) {
// Adding preloaded torrent /// Adding preloaded torrent...
m_loadedMetadata.remove(hash); m_addingTorrents.insert(hash, params);
libt::torrent_handle handle = m_nativeSession->find_torrent(hash);
--m_extraLimit; 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 { try {
handle.auto_managed(false); handle.auto_managed(false);
// Preloaded torrent is in "Upload mode" so we need to disable it
// otherwise the torrent never be downloaded (until application restart)
handle.set_upload_mode(false);
handle.pause(); handle.pause();
} }
catch (std::exception &) {} catch (std::exception &) {}
adjustLimits();
// use common 2nd step of torrent addition
m_addingTorrents.insert(hash, params);
createTorrentHandle(handle);
return true; return true;
} }
@ -2225,9 +2219,12 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
else else
p.storage_mode = libt::storage_mode_sparse; p.storage_mode = libt::storage_mode_sparse;
p.flags |= libt::add_torrent_params::flag_paused; // Start in pause
p.flags &= ~libt::add_torrent_params::flag_auto_managed; // Because it is added in paused state
p.flags &= ~libt::add_torrent_params::flag_duplicate_is_error; // Already checked p.flags &= ~libt::add_torrent_params::flag_duplicate_is_error; // Already checked
// Make sure the torrent will be initially checked and then paused
// to perform some service jobs on it. We will start it if needed.
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;
// Seeding mode // Seeding mode
// Skip checking and directly start seeding (new in libtorrent v0.15) // Skip checking and directly start seeding (new in libtorrent v0.15)
@ -2239,7 +2236,15 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
if (!fromMagnetUri) { if (!fromMagnetUri) {
if (params.restored) { if (params.restored) {
// Set torrent fast resume data // Set torrent fast resume data
p.resume_data = {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<char> {patchedFastresumeData.constData()
, (patchedFastresumeData.constData() + patchedFastresumeData.size())};
p.flags |= libt::add_torrent_params::flag_use_resume_save_path; p.flags |= libt::add_torrent_params::flag_use_resume_save_path;
} }
else { else {
@ -2247,12 +2252,6 @@ bool Session::addTorrent_impl(CreateTorrentParams params, const MagnetUri &magne
} }
} }
if (params.restored && !params.paused) {
// Make sure the torrent will restored in "paused" state
// Then we will start it if needed
p.flags |= libt::add_torrent_params::flag_stop_when_ready;
}
// Limits // Limits
p.max_connections = maxConnectionsPerTorrent(); p.max_connections = maxConnectionsPerTorrent();
p.max_uploads = maxUploadsPerTorrent(); p.max_uploads = maxUploadsPerTorrent();
@ -4148,10 +4147,7 @@ void Session::handleAlert(libt::alert *a)
case libt::tracker_warning_alert::alert_type: case libt::tracker_warning_alert::alert_type:
case libt::fastresume_rejected_alert::alert_type: case libt::fastresume_rejected_alert::alert_type:
case libt::torrent_checked_alert::alert_type: case libt::torrent_checked_alert::alert_type:
dispatchTorrentAlert(a);
break;
case libt::metadata_received_alert::alert_type: case libt::metadata_received_alert::alert_type:
handleMetadataReceivedAlert(static_cast<libt::metadata_received_alert*>(a));
dispatchTorrentAlert(a); dispatchTorrentAlert(a);
break; break;
case libt::state_update_alert::alert_type: case libt::state_update_alert::alert_type:
@ -4211,8 +4207,19 @@ void Session::handleAlert(libt::alert *a)
void Session::dispatchTorrentAlert(libt::alert *a) void Session::dispatchTorrentAlert(libt::alert *a)
{ {
TorrentHandle *const torrent = m_torrents.value(static_cast<libt::torrent_alert*>(a)->handle.info_hash()); TorrentHandle *const torrent = m_torrents.value(static_cast<libt::torrent_alert*>(a)->handle.info_hash());
if (torrent) if (torrent) {
torrent->handleAlert(a); torrent->handleAlert(a);
return;
}
switch (a->type()) {
case libt::torrent_paused_alert::alert_type:
handleTorrentPausedAlert(static_cast<const libt::torrent_paused_alert*>(a));
break;
case libt::metadata_received_alert::alert_type:
handleMetadataReceivedAlert(static_cast<const libt::metadata_received_alert*>(a));
break;
}
} }
void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle) void Session::createTorrentHandle(const libt::torrent_handle &nativeHandle)
@ -4326,7 +4333,7 @@ void Session::handleTorrentDeleteFailedAlert(libt::torrent_delete_failed_alert *
} }
} }
void Session::handleMetadataReceivedAlert(libt::metadata_received_alert *p) void Session::handleMetadataReceivedAlert(const libt::metadata_received_alert *p)
{ {
InfoHash hash = p->handle.info_hash(); InfoHash hash = p->handle.info_hash();
@ -4338,6 +4345,27 @@ void Session::handleMetadataReceivedAlert(libt::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)
handle.set_upload_mode(false);
handle.auto_managed(true);
adjustLimits();
// use common 2nd step of torrent addition
createTorrentHandle(handle);
}
}
void Session::handleFileErrorAlert(libt::file_error_alert *p) void Session::handleFileErrorAlert(libt::file_error_alert *p)
{ {
TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash()); TorrentHandle *const torrent = m_torrents.value(p->handle.info_hash());

View file

@ -614,7 +614,8 @@ namespace BitTorrent
void dispatchTorrentAlert(libtorrent::alert *a); void dispatchTorrentAlert(libtorrent::alert *a);
void handleAddTorrentAlert(libtorrent::add_torrent_alert *p); void handleAddTorrentAlert(libtorrent::add_torrent_alert *p);
void handleStateUpdateAlert(libtorrent::state_update_alert *p); void handleStateUpdateAlert(libtorrent::state_update_alert *p);
void handleMetadataReceivedAlert(libtorrent::metadata_received_alert *p); void handleMetadataReceivedAlert(const libtorrent::metadata_received_alert *p);
void handleTorrentPausedAlert(const libtorrent::torrent_paused_alert *p);
void handleFileErrorAlert(libtorrent::file_error_alert *p); void handleFileErrorAlert(libtorrent::file_error_alert *p);
void handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p); void handleTorrentRemovedAlert(libtorrent::torrent_removed_alert *p);
void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert *p); void handleTorrentDeletedAlert(libtorrent::torrent_deleted_alert *p);

View file

@ -192,6 +192,7 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
, m_hasRootFolder(params.hasRootFolder) , m_hasRootFolder(params.hasRootFolder)
, m_needsToSetFirstLastPiecePriority(false) , m_needsToSetFirstLastPiecePriority(false)
, m_needsToStartForced(params.forced) , m_needsToStartForced(params.forced)
, m_pauseWhenReady(params.paused)
{ {
if (m_useAutoTMM) if (m_useAutoTMM)
m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category)); m_savePath = Utils::Fs::toNativePath(m_session->categorySavePath(m_category));
@ -217,18 +218,18 @@ TorrentHandle::TorrentHandle(Session *session, const libtorrent::torrent_handle
m_hasRootFolder = false; m_hasRootFolder = false;
} }
// "started" means "all initialization has completed and torrent has started regular processing". if (!hasMetadata()) {
// When torrent added/restored in "paused" state it become "started" immediately after construction. // There is nothing to prepare
// When it is added/restored in "resumed" state, it become "started" after it is really resumed if (!m_pauseWhenReady) {
// (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 // Resume torrent because it was added in "resumed" state
// but it's actually paused during initialization // but it's actually paused during initialization.
m_startupState = Starting; m_startupState = Starting;
resume(params.forced); resume(m_needsToStartForced);
}
else {
m_startupState = Started;
m_pauseWhenReady = false;
}
} }
} }
@ -1275,7 +1276,8 @@ void TorrentHandle::forceRecheck()
if (isPaused()) { if (isPaused()) {
m_nativeHandle.stop_when_ready(true); m_nativeHandle.stop_when_ready(true);
resume_impl(false); m_nativeHandle.auto_managed(true);
m_pauseWhenReady = true;
} }
} }
@ -1554,7 +1556,8 @@ void TorrentHandle::handleTorrentCheckedAlert(const libtorrent::torrent_checked_
Q_UNUSED(p); Q_UNUSED(p);
qDebug("\"%s\" have just finished checking", qUtf8Printable(name())); qDebug("\"%s\" have just finished checking", qUtf8Printable(name()));
if (m_startupState == NotStarted) { if (m_startupState == Preparing) {
if (!m_pauseWhenReady) {
if (!m_hasMissingFiles) { if (!m_hasMissingFiles) {
// Resume torrent because it was added in "resumed" state // Resume torrent because it was added in "resumed" state
// but it's actually paused during initialization. // but it's actually paused during initialization.
@ -1562,11 +1565,15 @@ void TorrentHandle::handleTorrentCheckedAlert(const libtorrent::torrent_checked_
resume(m_needsToStartForced); resume(m_needsToStartForced);
} }
else { else {
// Torrent that has missing files is marked as "started" // Torrent that has missing files is paused.
// but it remains paused.
m_startupState = Started; m_startupState = Started;
} }
} }
else {
m_startupState = Started;
m_pauseWhenReady = false;
}
}
updateStatus(); updateStatus();
@ -1588,10 +1595,10 @@ void TorrentHandle::handleTorrentFinishedAlert(const libtorrent::torrent_finishe
Q_UNUSED(p); Q_UNUSED(p);
qDebug("Got a torrent finished alert for \"%s\"", qUtf8Printable(name())); qDebug("Got a torrent finished alert for \"%s\"", qUtf8Printable(name()));
qDebug("Torrent has seed status: %s", m_hasSeedStatus ? "yes" : "no"); qDebug("Torrent has seed status: %s", m_hasSeedStatus ? "yes" : "no");
m_hasMissingFiles = false;
if (m_hasSeedStatus) return; if (m_hasSeedStatus) return;
updateStatus(); updateStatus();
m_hasMissingFiles = false;
m_hasSeedStatus = true; m_hasSeedStatus = true;
adjustActualSavePath(); adjustActualSavePath();
@ -1615,10 +1622,15 @@ void TorrentHandle::handleTorrentPausedAlert(const libtorrent::torrent_paused_al
Q_UNUSED(p); Q_UNUSED(p);
if (m_startupState == Started) { if (m_startupState == Started) {
if (!m_pauseWhenReady) {
updateStatus(); updateStatus();
m_speedMonitor.reset(); m_speedMonitor.reset();
m_session->handleTorrentPaused(this); m_session->handleTorrentPaused(this);
} }
else {
m_pauseWhenReady = false;
}
}
} }
void TorrentHandle::handleTorrentResumedAlert(const libtorrent::torrent_resumed_alert *p) void TorrentHandle::handleTorrentResumedAlert(const libtorrent::torrent_resumed_alert *p)
@ -1639,8 +1651,8 @@ void TorrentHandle::handleSaveResumeDataAlert(const libtorrent::save_resume_data
libtorrent::entry &resumeData = useDummyResumeData ? dummyEntry : *(p->resume_data); libtorrent::entry &resumeData = useDummyResumeData ? dummyEntry : *(p->resume_data);
if (useDummyResumeData) { if (useDummyResumeData) {
resumeData["qBt-magnetUri"] = toMagnetUri().toStdString(); resumeData["qBt-magnetUri"] = toMagnetUri().toStdString();
resumeData["qBt-paused"] = isPaused(); resumeData["paused"] = isPaused();
resumeData["qBt-forced"] = isForced(); resumeData["auto_managed"] = m_nativeStatus.auto_managed;
// Both firstLastPiecePriority and sequential need to be stored in the // Both firstLastPiecePriority and sequential need to be stored in the
// resume data if there is no metadata, otherwise they won't be // resume data if there is no metadata, otherwise they won't be
// restored if qBittorrent quits before the metadata are retrieved: // restored if qBittorrent quits before the metadata are retrieved:
@ -1662,6 +1674,14 @@ void TorrentHandle::handleSaveResumeDataAlert(const libtorrent::save_resume_data
resumeData["qBt-queuePosition"] = (nativeHandle().queue_position() + 1); // qBt starts queue at 1 resumeData["qBt-queuePosition"] = (nativeHandle().queue_position() + 1); // qBt starts queue at 1
resumeData["qBt-hasRootFolder"] = m_hasRootFolder; 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); m_session->handleTorrentResumeDataReady(this, resumeData);
} }

View file

@ -476,12 +476,15 @@ namespace BitTorrent
enum StartupState enum StartupState
{ {
NotStarted, Preparing, // torrent is preparing to start regular processing
Starting, Starting, // torrent is prepared and starting to perform regular processing
Started 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; bool m_unchecked = false;
}; };
} }