Blob Blame History Raw
From f551b4adbff0d59557d61867d0b6518c50f5a73f Mon Sep 17 00:00:00 2001
From: Charles Kerr <charles@charleskerr.com>
Date: Mon, 13 Feb 2023 12:33:33 -0600
Subject: [PATCH] fix: magnet links are always paused when added (#4856)

---
 libtransmission/peer-mgr.cc       |   8 +-
 libtransmission/resume.cc         |   6 +-
 libtransmission/torrent-magnet.cc |  34 +++----
 libtransmission/torrent-magnet.h  |   2 +
 libtransmission/torrent.cc        | 148 ++++++++++++++----------------
 libtransmission/torrent.h         |   9 +-
 libtransmission/transmission.h    |   2 +-
 macosx/Torrent.mm                 |   2 +-
 8 files changed, 101 insertions(+), 110 deletions(-)

diff --git a/libtransmission/peer-mgr.cc b/libtransmission/peer-mgr.cc
index 4cf1d332c8..8e826dc3fa 100644
--- a/libtransmission/peer-mgr.cc
+++ b/libtransmission/peer-mgr.cc
@@ -43,6 +43,7 @@
 #include "session.h"
 #include "timer.h"
 #include "torrent.h"
+#include "torrent-magnet.h"
 #include "tr-assert.h"
 #include "tr-utp.h"
 #include "utils.h"
@@ -2391,8 +2392,11 @@ void tr_peerMgr::bandwidthPulse()
     session->top_bandwidth_.allocate(Msec);
 
     // torrent upkeep
-    auto& torrents = session->torrents();
-    std::for_each(std::begin(torrents), std::end(torrents), [](auto* tor) { tor->do_idle_work(); });
+    for (auto* const tor : session->torrents())
+    {
+        tor->do_idle_work();
+        tr_torrentMagnetDoIdleWork(tor);
+    }
 
     /* pump the queues */
     queuePulse(session, TR_UP);
diff --git a/libtransmission/resume.cc b/libtransmission/resume.cc
index 80c00dbd28..9716ae876c 100644
--- a/libtransmission/resume.cc
+++ b/libtransmission/resume.cc
@@ -712,7 +712,7 @@ auto loadFromFile(tr_torrent* tor, tr_resume::fields_t fields_to_load, bool* did
 
     if (auto val = bool{}; (fields_to_load & tr_resume::Run) != 0 && tr_variantDictFindBool(&top, TR_KEY_paused, &val))
     {
-        tor->isRunning = !val;
+        tor->start_when_stable = !val;
         fields_loaded |= tr_resume::Run;
     }
 
@@ -831,7 +831,7 @@ auto setFromCtor(tr_torrent* tor, tr_resume::fields_t fields, tr_ctor const* cto
     {
         if (auto is_paused = bool{}; tr_ctorGetPaused(ctor, mode, &is_paused))
         {
-            tor->isRunning = !is_paused;
+            tor->start_when_stable = !is_paused;
             ret |= tr_resume::Run;
         }
     }
@@ -907,7 +907,7 @@ void save(tr_torrent* tor)
     tr_variantDictAddInt(&top, TR_KEY_uploaded, tor->uploadedPrev + tor->uploadedCur);
     tr_variantDictAddInt(&top, TR_KEY_max_peers, tor->peerLimit());
     tr_variantDictAddInt(&top, TR_KEY_bandwidth_priority, tor->getPriority());
-    tr_variantDictAddBool(&top, TR_KEY_paused, !tor->isRunning && !tor->isQueued());
+    tr_variantDictAddBool(&top, TR_KEY_paused, !tor->start_when_stable);
     savePeers(&top, tor);
 
     if (tor->hasMetainfo())
diff --git a/libtransmission/torrent-magnet.cc b/libtransmission/torrent-magnet.cc
index 1d91cbfef1..c32defb433 100644
--- a/libtransmission/torrent-magnet.cc
+++ b/libtransmission/torrent-magnet.cc
@@ -176,13 +176,6 @@ bool tr_torrentUseMetainfoFromFile(
         delete tor->incompleteMetadata;
         tor->incompleteMetadata = nullptr;
     }
-    tor->isStopping = true;
-    tor->magnetVerify = true;
-    if (tor->session->shouldPauseAddedTorrents())
-    {
-        tor->startAfterVerify = false;
-    }
-    tor->markEdited();
 
     return true;
 }
@@ -310,13 +303,6 @@ void on_have_all_metainfo(tr_torrent* tor, tr_incomplete_metadata* m)
     {
         delete tor->incompleteMetadata;
         tor->incompleteMetadata = nullptr;
-        tor->isStopping = true;
-        tor->magnetVerify = true;
-        if (tor->session->shouldPauseAddedTorrents() && !tor->magnetStartAfterVerify)
-        {
-            tor->startAfterVerify = false;
-        }
-        tor->markEdited();
     }
     else /* drat. */
     {
@@ -340,6 +326,19 @@ void on_have_all_metainfo(tr_torrent* tor, tr_incomplete_metadata* m)
 } // namespace set_metadata_piece_helpers
 } // namespace
 
+void tr_torrentMagnetDoIdleWork(tr_torrent* const tor)
+{
+    using namespace set_metadata_piece_helpers;
+
+    TR_ASSERT(tr_isTorrent(tor));
+
+    if (auto* const m = tor->incompleteMetadata; m != nullptr && std::empty(m->pieces_needed))
+    {
+        tr_logAddDebugTor(tor, fmt::format("we now have all the metainfo!"));
+        on_have_all_metainfo(tor, m);
+    }
+}
+
 void tr_torrentSetMetadataPiece(tr_torrent* tor, int piece, void const* data, size_t len)
 {
     using namespace set_metadata_piece_helpers;
@@ -384,13 +383,6 @@ void tr_torrentSetMetadataPiece(tr_torrent* tor, int piece, void const* data, si
 
     needed.erase(iter);
     tr_logAddDebugTor(tor, fmt::format("saving metainfo piece {}... {} remain", piece, std::size(needed)));
-
-    // are we done?
-    if (std::empty(needed))
-    {
-        tr_logAddDebugTor(tor, fmt::format("metainfo piece {} was the last one", piece));
-        on_have_all_metainfo(tor, m);
-    }
 }
 
 // ---
diff --git a/libtransmission/torrent-magnet.h b/libtransmission/torrent-magnet.h
index 8af953bec9..cb5d7e1fcb 100644
--- a/libtransmission/torrent-magnet.h
+++ b/libtransmission/torrent-magnet.h
@@ -33,6 +33,8 @@ bool tr_torrentSetMetadataSizeHint(tr_torrent* tor, int64_t metadata_size);
 
 double tr_torrentGetMetadataPercent(tr_torrent const* tor);
 
+void tr_torrentMagnetDoIdleWork(tr_torrent* tor);
+
 bool tr_torrentUseMetainfoFromFile(
     tr_torrent* tor,
     tr_torrent_metainfo const* metainfo,
diff --git a/libtransmission/torrent.cc b/libtransmission/torrent.cc
index 1e74f8152b..4b5a0b1dea 100644
--- a/libtransmission/torrent.cc
+++ b/libtransmission/torrent.cc
@@ -830,6 +830,8 @@ void torrentStart(tr_torrent* tor, torrent_start_opts opts)
 {
     using namespace start_stop_helpers;
 
+    auto const lock = tor->unique_lock();
+
     switch (tor->activity())
     {
     case TR_STATUS_SEED:
@@ -849,7 +851,6 @@ void torrentStart(tr_torrent* tor, torrent_start_opts opts)
     case TR_STATUS_CHECK_WAIT:
         /* verifying right now... wait until that's done so
          * we'll know what completeness to use/announce */
-        tor->startAfterVerify = true;
         return;
 
     case TR_STATUS_STOPPED:
@@ -868,9 +869,6 @@ void torrentStart(tr_torrent* tor, torrent_start_opts opts)
         return;
     }
 
-    /* otherwise, start it now... */
-    auto const lock = tor->unique_lock();
-
     /* allow finished torrents to be resumed */
     if (tr_torrentIsSeedRatioDone(tor))
     {
@@ -895,6 +893,9 @@ void torrentStop(tr_torrent* const tor)
     TR_ASSERT(tor->session->amInSessionThread());
     auto const lock = tor->unique_lock();
 
+    tor->isRunning = false;
+    tor->isStopping = false;
+
     if (!tor->session->isClosing())
     {
         tr_logAddInfoTor(tor, _("Pausing torrent"));
@@ -913,16 +914,6 @@ void torrentStop(tr_torrent* const tor)
     }
 
     torrentSetQueued(tor, false);
-
-    if (tor->magnetVerify)
-    {
-        tor->magnetVerify = false;
-        tr_logAddTraceTor(tor, "Magnet Verify");
-        tor->refreshCurrentDir();
-        tr_torrentVerify(tor);
-
-        callScriptIfEnabled(tor, TR_SCRIPT_ON_TORRENT_ADDED);
-    }
 }
 } // namespace
 
@@ -935,8 +926,7 @@ void tr_torrentStop(tr_torrent* tor)
 
     auto const lock = tor->unique_lock();
 
-    tor->isRunning = false;
-    tor->isStopping = false;
+    tor->start_when_stable = false;
     tor->setDirty();
     tor->session->runInSessionThread(torrentStop, tor);
 }
@@ -965,7 +955,6 @@ void tr_torrentFreeInSessionThread(tr_torrent* tor)
         tr_logAddInfoTor(tor, _("Removing torrent"));
     }
 
-    tor->magnetVerify = false;
     torrentStop(tor);
 
     if (tor->isDeleting)
@@ -975,7 +964,6 @@ void tr_torrentFreeInSessionThread(tr_torrent* tor)
         tr_torrent_metainfo::removeFile(tor->session->resumeDir(), tor->name(), tor->infoHashString(), ".resume"sv);
     }
 
-    tor->isRunning = false;
     freeTorrent(tor);
 }
 
@@ -1036,6 +1024,34 @@ void torrentInitFromInfoDict(tr_torrent* tor)
     tor->checked_pieces_ = tr_bitfield{ size_t(tor->pieceCount()) };
 }
 
+void on_metainfo_completed(tr_torrent* tor)
+{
+    // we can look for files now that we know what files are in the torrent
+    tor->refreshCurrentDir();
+
+    callScriptIfEnabled(tor, TR_SCRIPT_ON_TORRENT_ADDED);
+
+    if (tor->session->shouldFullyVerifyAddedTorrents() || !isNewTorrentASeed(tor))
+    {
+        tr_torrentVerify(tor);
+    }
+    else
+    {
+        tor->completion.setHasAll();
+        tor->doneDate = tor->addedDate;
+        tor->recheckCompleteness();
+
+        if (tor->start_when_stable)
+        {
+            torrentStart(tor, {});
+        }
+        else if (tor->isRunning)
+        {
+            tr_torrentStop(tor);
+        }
+    }
+}
+
 void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
 {
     tr_session* session = tr_ctorGetSession(ctor);
@@ -1081,7 +1097,7 @@ void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
     tor->anyDate = now;
 
     tr_resume::fields_t loaded = {};
-    if (tor->hasMetainfo())
+
     {
         // tr_resume::load() calls a lot of tr_torrentSetFoo() methods
         // that set things as dirty, but... these settings being loaded are
@@ -1106,9 +1122,6 @@ void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
 
     tor->refreshCurrentDir();
 
-    bool const do_start = tor->isRunning;
-    tor->isRunning = false;
-
     if ((loaded & tr_resume::Speedlimit) == 0)
     {
         tor->useSpeedLimit(TR_UP, false);
@@ -1174,37 +1187,14 @@ void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
 
     tor->torrent_announcer = session->announcer_->addTorrent(tor, &tr_torrent::onTrackerResponse);
 
-    if (is_new_torrent)
+    if (auto const has_metainfo = tor->hasMetainfo(); is_new_torrent && has_metainfo)
     {
-        if (tor->hasMetainfo())
-        {
-            callScriptIfEnabled(tor, TR_SCRIPT_ON_TORRENT_ADDED);
-        }
-
-        if (!tor->hasMetainfo() && !do_start)
-        {
-            auto opts = torrent_start_opts{};
-            opts.bypass_queue = true;
-            opts.has_local_data = has_local_data;
-            torrentStart(tor, opts);
-        }
-        else if (!session->shouldFullyVerifyAddedTorrents() && isNewTorrentASeed(tor))
-        {
-            tor->completion.setHasAll();
-            tor->doneDate = tor->addedDate;
-            tor->recheckCompleteness();
-        }
-        else
-        {
-            tor->startAfterVerify = do_start;
-            tr_torrentVerify(tor);
-        }
+        on_metainfo_completed(tor);
     }
-    else if (do_start)
+    else if (tor->start_when_stable || !has_metainfo)
     {
-        // if checked_pieces_ got populated from the loading the resume
-        // file above, then torrentStart doesn't need to check again
         auto opts = torrent_start_opts{};
+        opts.bypass_queue = !has_metainfo; // to fetch metainfo from peers
         opts.has_local_data = has_local_data;
         torrentStart(tor, opts);
     }
@@ -1216,16 +1206,20 @@ void torrentInit(tr_torrent* tor, tr_ctor const* ctor)
 } // namespace torrent_init_helpers
 } // namespace
 
-void tr_torrent::setMetainfo(tr_torrent_metainfo const& tm)
+void tr_torrent::setMetainfo(tr_torrent_metainfo tm)
 {
     using namespace torrent_init_helpers;
 
-    metainfo_ = tm;
+    TR_ASSERT(!hasMetainfo());
+    metainfo_ = std::move(tm);
 
     torrentInitFromInfoDict(this);
     tr_peerMgrOnTorrentGotMetainfo(this);
     session->onMetadataCompleted(this);
     this->setDirty();
+    this->markEdited();
+
+    on_metainfo_completed(this);
 }
 
 tr_torrent* tr_torrentNew(tr_ctor* ctor, tr_torrent** setme_duplicate_of)
@@ -1763,33 +1757,35 @@ void tr_torrentAmountFinished(tr_torrent const* tor, float* tabs, int n_tabs)
 
 // --- Start/Stop Callback
 
-void tr_torrentStart(tr_torrent* tor)
+namespace
+{
+void tr_torrentStartImpl(tr_torrent* tor, bool bypass_queue)
 {
-    if (tr_isTorrent(tor))
+    if (!tr_isTorrent(tor))
     {
-        tor->startAfterVerify = true;
-        torrentStart(tor, {});
+        return;
     }
+
+    tor->start_when_stable = true;
+    auto opts = torrent_start_opts{};
+    opts.bypass_queue = bypass_queue;
+    torrentStart(tor, opts);
+}
+} // namespace
+
+void tr_torrentStart(tr_torrent* tor)
+{
+    tr_torrentStartImpl(tor, false);
 }
 
 void tr_torrentStartNow(tr_torrent* tor)
 {
-    if (tr_isTorrent(tor))
-    {
-        auto opts = torrent_start_opts{};
-        opts.bypass_queue = true;
-        torrentStart(tor, opts);
-    }
+    tr_torrentStartImpl(tor, true);
 }
 
 void tr_torrentStartMagnet(tr_torrent* tor)
 {
-    if (tr_isTorrent(tor))
-    {
-        tor->magnetStartAfterVerify = true;
-        tor->startAfterVerify = true;
-        torrentStart(tor, {});
-    }
+    tr_torrentStart(tor);
 }
 
 // ---
@@ -1809,10 +1805,8 @@ void onVerifyDoneThreadFunc(tr_torrent* const tor)
 
     tor->recheckCompleteness();
 
-    if (tor->startAfterVerify)
+    if (tor->start_when_stable)
     {
-        tor->startAfterVerify = false;
-
         auto opts = torrent_start_opts{};
         opts.has_local_data = !tor->checked_pieces_.hasNone();
         torrentStart(tor, opts);
@@ -1832,20 +1826,18 @@ void verifyTorrent(tr_torrent* const tor)
     /* if the torrent's already being verified, stop it */
     tor->session->verifyRemove(tor);
 
-    bool const start_after = (tor->isRunning || tor->startAfterVerify) && !tor->isStopping;
-
-    if (tor->isRunning)
+    if (!tor->hasMetainfo())
     {
-        tr_torrentStop(tor);
+        return;
     }
 
-    if (setLocalErrorIfFilesDisappeared(tor))
+    if (tor->isRunning)
     {
-        tor->startAfterVerify = false;
+        torrentStop(tor);
     }
-    else
+
+    if (!setLocalErrorIfFilesDisappeared(tor))
     {
-        tor->startAfterVerify = start_after;
         tor->session->verifyAdd(tor);
     }
 }
diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h
index deebb77337..a1a3031aa8 100644
--- a/libtransmission/torrent.h
+++ b/libtransmission/torrent.h
@@ -109,7 +109,8 @@ struct tr_torrent final : public tr_completion::torrent_view
     // but more refactoring is needed before that can happen
     // because much of tr_torrent's impl is in the non-member C bindings
 
-    void setMetainfo(tr_torrent_metainfo const& tm);
+    // Used to add metainfo to a magnet torrent.
+    void setMetainfo(tr_torrent_metainfo tm);
 
     [[nodiscard]] auto unique_lock() const
     {
@@ -903,10 +904,10 @@ struct tr_torrent final : public tr_completion::torrent_view
     bool is_queued = false;
     bool isRunning = false;
     bool isStopping = false;
-    bool startAfterVerify = false;
-    bool magnetStartAfterVerify = false;
 
-    bool magnetVerify = false;
+    // start the torrent after all the startup scaffolding is done,
+    // e.g. fetching metadata from peers and/or verifying the torrent
+    bool start_when_stable = false;
 
 private:
     [[nodiscard]] constexpr bool isPieceTransferAllowed(tr_direction direction) const noexcept
diff --git a/libtransmission/transmission.h b/libtransmission/transmission.h
index 11bb8743dd..82bf1d5e54 100644
--- a/libtransmission/transmission.h
+++ b/libtransmission/transmission.h
@@ -601,7 +601,7 @@ void tr_sessionSetAntiBruteForceEnabled(tr_session* session, bool enabled);
 /** @brief Like `tr_torrentStart()`, but resumes right away regardless of the queues. */
 void tr_torrentStartNow(tr_torrent* tor);
 
-/** @brief Like tr_torrentStart(), but sets magnetStartAfterVerify to true. */
+/** @brief DEPRECATED. Equivalent to tr_torrentStart(). Use that instead. */
 void tr_torrentStartMagnet(tr_torrent*);
 
 /** @brief Return the queued torrent's position in the queue it's in. [0...n) */
diff --git a/macosx/Torrent.mm b/macosx/Torrent.mm
index bd5ad7972f..98e69882d3 100644
--- a/macosx/Torrent.mm
+++ b/macosx/Torrent.mm
@@ -280,7 +280,7 @@ - (void)startMagnetTransferAfterMetaDownload
 {
     if ([self alertForRemainingDiskSpace])
     {
-        tr_torrentStartMagnet(self.fHandle);
+        tr_torrentStart(self.fHandle);
         [self update];
 
         //capture, specifically, stop-seeding settings changing to unlimited