parent
74a70f193b
commit
0e01bbd180
@ -0,0 +1,363 @@
|
|||||||
|
From ba97fb792c248320edd6353593feaff91b1155ef Mon Sep 17 00:00:00 2001
|
||||||
|
From: Charles Kerr <charles@charleskerr.com>
|
||||||
|
Date: Tue, 14 Feb 2023 10:30:54 -0600
|
||||||
|
Subject: [PATCH 1/3] fix: processing of block fragments that arrive
|
||||||
|
out-of-order from request
|
||||||
|
|
||||||
|
Fixes 4.0.0 regression on torrents whose pieces did not align on block boundaries.
|
||||||
|
---
|
||||||
|
libtransmission/cache.cc | 2 +-
|
||||||
|
libtransmission/cache.h | 2 +-
|
||||||
|
libtransmission/peer-msgs.cc | 96 ++++++++++++++++++------------------
|
||||||
|
libtransmission/torrent.cc | 11 ++++-
|
||||||
|
libtransmission/webseed.cc | 2 +-
|
||||||
|
5 files changed, 60 insertions(+), 53 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/libtransmission/cache.cc b/libtransmission/cache.cc
|
||||||
|
index ef446b244c..f7ae03dcc1 100644
|
||||||
|
--- a/libtransmission/cache.cc
|
||||||
|
+++ b/libtransmission/cache.cc
|
||||||
|
@@ -140,7 +140,7 @@ Cache::Cache(tr_torrents& torrents, int64_t max_bytes)
|
||||||
|
|
||||||
|
// ---
|
||||||
|
|
||||||
|
-int Cache::writeBlock(tr_torrent_id_t tor_id, tr_block_index_t block, std::unique_ptr<std::vector<uint8_t>>& writeme)
|
||||||
|
+int Cache::writeBlock(tr_torrent_id_t tor_id, tr_block_index_t block, std::unique_ptr<std::vector<uint8_t>> writeme)
|
||||||
|
{
|
||||||
|
auto const key = Key{ tor_id, block };
|
||||||
|
auto iter = std::lower_bound(std::begin(blocks_), std::end(blocks_), key, CompareCacheBlockByKey{});
|
||||||
|
diff --git a/libtransmission/cache.h b/libtransmission/cache.h
|
||||||
|
index e25deb2328..9d6455610b 100644
|
||||||
|
--- a/libtransmission/cache.h
|
||||||
|
+++ b/libtransmission/cache.h
|
||||||
|
@@ -36,7 +36,7 @@ class Cache
|
||||||
|
}
|
||||||
|
|
||||||
|
// @return any error code from cacheTrim()
|
||||||
|
- int writeBlock(tr_torrent_id_t tor, tr_block_index_t block, std::unique_ptr<std::vector<uint8_t>>& writeme);
|
||||||
|
+ int writeBlock(tr_torrent_id_t tor, tr_block_index_t block, std::unique_ptr<std::vector<uint8_t>> writeme);
|
||||||
|
|
||||||
|
int readBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t len, uint8_t* setme);
|
||||||
|
int prefetchBlock(tr_torrent* torrent, tr_block_info::Location loc, uint32_t len);
|
||||||
|
diff --git a/libtransmission/peer-msgs.cc b/libtransmission/peer-msgs.cc
|
||||||
|
index 837dbb4b76..05864fea20 100644
|
||||||
|
--- a/libtransmission/peer-msgs.cc
|
||||||
|
+++ b/libtransmission/peer-msgs.cc
|
||||||
|
@@ -188,7 +188,20 @@ struct tr_incoming
|
||||||
|
uint8_t id = 0; // the protocol message, e.g. BtPeerMsgs::Piece
|
||||||
|
uint32_t length = 0; // the full message payload length. Includes the +1 for id length
|
||||||
|
std::optional<peer_request> block_req; // metadata for incoming blocks
|
||||||
|
- std::map<tr_block_index_t, std::unique_ptr<std::vector<uint8_t>>> block_buf; // piece data for incoming blocks
|
||||||
|
+
|
||||||
|
+ struct incoming_piece_data
|
||||||
|
+ {
|
||||||
|
+ explicit incoming_piece_data(uint32_t block_size)
|
||||||
|
+ : buf{ std::make_unique<std::vector<uint8_t>>(block_size) }
|
||||||
|
+ {
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ uint32_t n_bytes_received = 0;
|
||||||
|
+
|
||||||
|
+ std::unique_ptr<std::vector<uint8_t>> buf;
|
||||||
|
+ };
|
||||||
|
+
|
||||||
|
+ std::map<tr_block_index_t, incoming_piece_data> blocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
class tr_peerMsgsImpl;
|
||||||
|
@@ -1397,7 +1410,7 @@ bool messageLengthIsCorrect(tr_peerMsgsImpl const* msg, uint8_t id, uint32_t len
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
-int clientGotBlock(tr_peerMsgsImpl* msgs, std::unique_ptr<std::vector<uint8_t>>& block_data, tr_block_index_t block);
|
||||||
|
+int clientGotBlock(tr_peerMsgsImpl* msgs, std::unique_ptr<std::vector<uint8_t>> block_data, tr_block_index_t block);
|
||||||
|
|
||||||
|
ReadState readBtPiece(tr_peerMsgsImpl* msgs, size_t inlen, size_t* setme_piece_bytes_read)
|
||||||
|
{
|
||||||
|
@@ -1406,7 +1419,8 @@ ReadState readBtPiece(tr_peerMsgsImpl* msgs, size_t inlen, size_t* setme_piece_b
|
||||||
|
logtrace(msgs, "In readBtPiece");
|
||||||
|
|
||||||
|
// If this is the first we've seen of the piece data, parse out the header
|
||||||
|
- if (!msgs->incoming.block_req)
|
||||||
|
+ auto& incoming = msgs->incoming;
|
||||||
|
+ if (!incoming.block_req)
|
||||||
|
{
|
||||||
|
if (inlen < 8)
|
||||||
|
{
|
||||||
|
@@ -1416,67 +1430,55 @@ ReadState readBtPiece(tr_peerMsgsImpl* msgs, size_t inlen, size_t* setme_piece_b
|
||||||
|
auto req = peer_request{};
|
||||||
|
msgs->io->read_uint32(&req.index);
|
||||||
|
msgs->io->read_uint32(&req.offset);
|
||||||
|
- req.length = msgs->incoming.length - 9;
|
||||||
|
+ req.length = incoming.length - 9;
|
||||||
|
logtrace(msgs, fmt::format(FMT_STRING("got incoming block header {:d}:{:d}->{:d}"), req.index, req.offset, req.length));
|
||||||
|
- msgs->incoming.block_req = req;
|
||||||
|
+ incoming.block_req = req;
|
||||||
|
return READ_NOW;
|
||||||
|
}
|
||||||
|
|
||||||
|
- auto& req = msgs->incoming.block_req;
|
||||||
|
+ auto& req = incoming.block_req;
|
||||||
|
auto const loc = msgs->torrent->pieceLoc(req->index, req->offset);
|
||||||
|
auto const block = loc.block;
|
||||||
|
auto const block_size = msgs->torrent->blockSize(block);
|
||||||
|
- auto& block_buf = msgs->incoming.block_buf[block];
|
||||||
|
- if (!block_buf)
|
||||||
|
- {
|
||||||
|
- block_buf = std::make_unique<std::vector<uint8_t>>();
|
||||||
|
- block_buf->reserve(block_size);
|
||||||
|
- }
|
||||||
|
|
||||||
|
- // read in another chunk of data
|
||||||
|
- auto const n_left_in_block = block_size - std::size(*block_buf);
|
||||||
|
- auto const n_left_in_req = size_t{ req->length };
|
||||||
|
- auto const n_to_read = std::min({ n_left_in_block, n_left_in_req, inlen });
|
||||||
|
- auto const old_length = std::size(*block_buf);
|
||||||
|
- block_buf->resize(old_length + n_to_read);
|
||||||
|
- msgs->io->read_bytes(&((*block_buf)[old_length]), n_to_read);
|
||||||
|
+ auto const n_this_pass = std::min(size_t{ req->length }, inlen);
|
||||||
|
+ TR_ASSERT(loc.block_offset + n_this_pass <= block_size);
|
||||||
|
|
||||||
|
- msgs->publish(tr_peer_event::GotPieceData(n_to_read));
|
||||||
|
- *setme_piece_bytes_read += n_to_read;
|
||||||
|
- logtrace(
|
||||||
|
- msgs,
|
||||||
|
- fmt::format(
|
||||||
|
- FMT_STRING("got {:d} bytes for block {:d}:{:d}->{:d} ... {:d} remain in req, {:d} remain in block"),
|
||||||
|
- n_to_read,
|
||||||
|
- req->index,
|
||||||
|
- req->offset,
|
||||||
|
- req->length,
|
||||||
|
- req->length,
|
||||||
|
- block_size - std::size(*block_buf)));
|
||||||
|
-
|
||||||
|
- // if we didn't read enough to finish off the request,
|
||||||
|
- // update the table and wait for more
|
||||||
|
- if (n_to_read < n_left_in_req)
|
||||||
|
- {
|
||||||
|
- auto new_loc = msgs->torrent->byteLoc(loc.byte + n_to_read);
|
||||||
|
+ auto& incoming_block = incoming.blocks.try_emplace(block, block_size).first->second;
|
||||||
|
+ msgs->io->read_bytes(std::data(*incoming_block.buf) + loc.block_offset, n_this_pass);
|
||||||
|
+
|
||||||
|
+ msgs->publish(tr_peer_event::GotPieceData(n_this_pass));
|
||||||
|
+ *setme_piece_bytes_read += n_this_pass;
|
||||||
|
+ incoming_block.n_bytes_received += n_this_pass;
|
||||||
|
+ TR_ASSERT(incoming_block.n_bytes_received <= block_size);
|
||||||
|
+ logtrace(msgs, fmt::format("got {:d} bytes for req {:d}:{:d}->{:d}", n_this_pass, req->index, req->offset, req->length));
|
||||||
|
+
|
||||||
|
+ // if we haven't gotten the full response yet,
|
||||||
|
+ // update what part of `req` is unfulfilled and wait for more
|
||||||
|
+ if (n_this_pass < req->length)
|
||||||
|
+ {
|
||||||
|
+ req->length -= n_this_pass;
|
||||||
|
+ auto const new_loc = msgs->torrent->byteLoc(loc.byte + n_this_pass);
|
||||||
|
req->index = new_loc.piece;
|
||||||
|
req->offset = new_loc.piece_offset;
|
||||||
|
- req->length -= n_to_read;
|
||||||
|
return READ_LATER;
|
||||||
|
}
|
||||||
|
|
||||||
|
- // we've fully read this message
|
||||||
|
+ // we've got the entire response message
|
||||||
|
req.reset();
|
||||||
|
msgs->state = AwaitingBt::Length;
|
||||||
|
|
||||||
|
- // if we didn't read enough to finish off the block,
|
||||||
|
- // update the table and wait for more
|
||||||
|
- if (std::size(*block_buf) < block_size)
|
||||||
|
+ // if we haven't gotten the entire block yet, wait for more
|
||||||
|
+ if (incoming_block.n_bytes_received < block_size)
|
||||||
|
{
|
||||||
|
return READ_LATER;
|
||||||
|
}
|
||||||
|
|
||||||
|
- return clientGotBlock(msgs, block_buf, block) != 0 ? READ_ERR : READ_NOW;
|
||||||
|
+ // we've got the entire block, so send it along.
|
||||||
|
+ auto block_buf = std::move(incoming_block.buf);
|
||||||
|
+ incoming.blocks.erase(block); // note: invalidates `incoming_block` local
|
||||||
|
+ auto const ok = clientGotBlock(msgs, std::move(block_buf), block) == 0;
|
||||||
|
+ return ok ? READ_NOW : READ_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadState readBtMessage(tr_peerMsgsImpl* msgs, size_t inlen)
|
||||||
|
@@ -1744,7 +1746,7 @@ ReadState readBtMessage(tr_peerMsgsImpl* msgs, size_t inlen)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns 0 on success, or an errno on failure */
|
||||||
|
-int clientGotBlock(tr_peerMsgsImpl* msgs, std::unique_ptr<std::vector<uint8_t>>& block_data, tr_block_index_t const block)
|
||||||
|
+int clientGotBlock(tr_peerMsgsImpl* msgs, std::unique_ptr<std::vector<uint8_t>> block_data, tr_block_index_t const block)
|
||||||
|
{
|
||||||
|
TR_ASSERT(msgs != nullptr);
|
||||||
|
|
||||||
|
@@ -1760,7 +1762,6 @@ int clientGotBlock(tr_peerMsgsImpl* msgs, std::unique_ptr<std::vector<uint8_t>>&
|
||||||
|
if (std::size(*block_data) != msgs->torrent->blockSize(block))
|
||||||
|
{
|
||||||
|
logdbg(msgs, fmt::format("wrong block size: expected {:d}, got {:d}", n_expected, std::size(*block_data)));
|
||||||
|
- block_data->clear();
|
||||||
|
return EMSGSIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1769,7 +1770,6 @@ int clientGotBlock(tr_peerMsgsImpl* msgs, std::unique_ptr<std::vector<uint8_t>>&
|
||||||
|
if (!tr_peerMgrDidPeerRequest(msgs->torrent, msgs, block))
|
||||||
|
{
|
||||||
|
logdbg(msgs, "we didn't ask for this message...");
|
||||||
|
- block_data->clear();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1777,19 +1777,17 @@ int clientGotBlock(tr_peerMsgsImpl* msgs, std::unique_ptr<std::vector<uint8_t>>&
|
||||||
|
if (msgs->torrent->hasPiece(loc.piece))
|
||||||
|
{
|
||||||
|
logtrace(msgs, "we did ask for this message, but the piece is already complete...");
|
||||||
|
- block_data->clear();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NB: if writeBlock() fails the torrent may be paused.
|
||||||
|
// If this happens, `msgs` will be a dangling pointer and must no longer be used.
|
||||||
|
- if (auto const err = msgs->session->cache->writeBlock(tor->id(), block, block_data); err != 0)
|
||||||
|
+ if (auto const err = msgs->session->cache->writeBlock(tor->id(), block, std::move(block_data)); err != 0)
|
||||||
|
{
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
msgs->blame.set(loc.piece);
|
||||||
|
- msgs->incoming.block_buf.erase(block);
|
||||||
|
msgs->publish(tr_peer_event::GotBlock(tor->blockInfo(), block));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
diff --git a/libtransmission/torrent.cc b/libtransmission/torrent.cc
|
||||||
|
index 4b5a0b1dea..cd96bac2c3 100644
|
||||||
|
--- a/libtransmission/torrent.cc
|
||||||
|
+++ b/libtransmission/torrent.cc
|
||||||
|
@@ -2400,8 +2400,17 @@ void tr_torrentGotBlock(tr_torrent* tor, tr_block_index_t block)
|
||||||
|
tor->setDirty();
|
||||||
|
|
||||||
|
tor->completion.addBlock(block);
|
||||||
|
- if (auto const piece = tor->blockLoc(block).piece; tor->hasPiece(piece))
|
||||||
|
+
|
||||||
|
+ auto const block_loc = tor->blockLoc(block);
|
||||||
|
+ auto const first_piece = block_loc.piece;
|
||||||
|
+ auto const last_piece = tor->byteLoc(block_loc.byte + tor->blockSize(block) - 1).piece;
|
||||||
|
+ for (auto piece = first_piece; piece <= last_piece; ++piece)
|
||||||
|
{
|
||||||
|
+ if (!tor->hasPiece(piece))
|
||||||
|
+ {
|
||||||
|
+ continue;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
if (tor->checkPiece(piece))
|
||||||
|
{
|
||||||
|
onPieceCompleted(tor, piece);
|
||||||
|
diff --git a/libtransmission/webseed.cc b/libtransmission/webseed.cc
|
||||||
|
index 7f07dd149e..8136139d87 100644
|
||||||
|
--- a/libtransmission/webseed.cc
|
||||||
|
+++ b/libtransmission/webseed.cc
|
||||||
|
@@ -352,7 +352,7 @@ struct write_block_data
|
||||||
|
{
|
||||||
|
if (auto const* const tor = tr_torrentFindFromId(session_, tor_id_); tor != nullptr)
|
||||||
|
{
|
||||||
|
- session_->cache->writeBlock(tor_id_, block_, data_);
|
||||||
|
+ session_->cache->writeBlock(tor_id_, block_, std::move(data_));
|
||||||
|
webseed_->publish(tr_peer_event::GotBlock(tor->blockInfo(), block_));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
From 8a3418aae23e37d446a86b11a34679db0bfa99f7 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Charles Kerr <charles@charleskerr.com>
|
||||||
|
Date: Tue, 14 Feb 2023 10:59:16 -0600
|
||||||
|
Subject: [PATCH 2/3] fixup! fix: processing of block fragments that arrive
|
||||||
|
out-of-order from request
|
||||||
|
|
||||||
|
refactor: be more paranoid about duplicate requests at endgame.
|
||||||
|
---
|
||||||
|
libtransmission/peer-msgs.cc | 16 ++++++++++------
|
||||||
|
1 file changed, 10 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/libtransmission/peer-msgs.cc b/libtransmission/peer-msgs.cc
|
||||||
|
index 05864fea20..24e3359d65 100644
|
||||||
|
--- a/libtransmission/peer-msgs.cc
|
||||||
|
+++ b/libtransmission/peer-msgs.cc
|
||||||
|
@@ -22,6 +22,7 @@
|
||||||
|
|
||||||
|
#include "transmission.h"
|
||||||
|
|
||||||
|
+#include "bitfield.h"
|
||||||
|
#include "cache.h"
|
||||||
|
#include "completion.h"
|
||||||
|
#include "crypto-utils.h"
|
||||||
|
@@ -193,12 +194,12 @@ struct tr_incoming
|
||||||
|
{
|
||||||
|
explicit incoming_piece_data(uint32_t block_size)
|
||||||
|
: buf{ std::make_unique<std::vector<uint8_t>>(block_size) }
|
||||||
|
+ , have{ block_size }
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
- uint32_t n_bytes_received = 0;
|
||||||
|
-
|
||||||
|
std::unique_ptr<std::vector<uint8_t>> buf;
|
||||||
|
+ tr_bitfield have;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::map<tr_block_index_t, incoming_piece_data> blocks;
|
||||||
|
@@ -1443,19 +1444,22 @@ ReadState readBtPiece(tr_peerMsgsImpl* msgs, size_t inlen, size_t* setme_piece_b
|
||||||
|
|
||||||
|
auto const n_this_pass = std::min(size_t{ req->length }, inlen);
|
||||||
|
TR_ASSERT(loc.block_offset + n_this_pass <= block_size);
|
||||||
|
+ if (n_this_pass == 0)
|
||||||
|
+ {
|
||||||
|
+ return READ_LATER;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
auto& incoming_block = incoming.blocks.try_emplace(block, block_size).first->second;
|
||||||
|
msgs->io->read_bytes(std::data(*incoming_block.buf) + loc.block_offset, n_this_pass);
|
||||||
|
|
||||||
|
msgs->publish(tr_peer_event::GotPieceData(n_this_pass));
|
||||||
|
*setme_piece_bytes_read += n_this_pass;
|
||||||
|
- incoming_block.n_bytes_received += n_this_pass;
|
||||||
|
- TR_ASSERT(incoming_block.n_bytes_received <= block_size);
|
||||||
|
+ incoming_block.have.setSpan(loc.block_offset, loc.block_offset + n_this_pass);
|
||||||
|
logtrace(msgs, fmt::format("got {:d} bytes for req {:d}:{:d}->{:d}", n_this_pass, req->index, req->offset, req->length));
|
||||||
|
|
||||||
|
// if we haven't gotten the full response yet,
|
||||||
|
// update what part of `req` is unfulfilled and wait for more
|
||||||
|
- if (n_this_pass < req->length)
|
||||||
|
+ if (req->length > n_this_pass)
|
||||||
|
{
|
||||||
|
req->length -= n_this_pass;
|
||||||
|
auto const new_loc = msgs->torrent->byteLoc(loc.byte + n_this_pass);
|
||||||
|
@@ -1469,7 +1473,7 @@ ReadState readBtPiece(tr_peerMsgsImpl* msgs, size_t inlen, size_t* setme_piece_b
|
||||||
|
msgs->state = AwaitingBt::Length;
|
||||||
|
|
||||||
|
// if we haven't gotten the entire block yet, wait for more
|
||||||
|
- if (incoming_block.n_bytes_received < block_size)
|
||||||
|
+ if (!incoming_block.have.hasAll())
|
||||||
|
{
|
||||||
|
return READ_LATER;
|
||||||
|
}
|
||||||
|
|
||||||
|
From d75445758c3e7dc666aba781111275d62acbf3b8 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Charles Kerr <charles@charleskerr.com>
|
||||||
|
Date: Tue, 14 Feb 2023 11:57:35 -0600
|
||||||
|
Subject: [PATCH 3/3] fixup! fix: processing of block fragments that arrive
|
||||||
|
out-of-order from request
|
||||||
|
|
||||||
|
chore: sync tests
|
||||||
|
---
|
||||||
|
tests/libtransmission/move-test.cc | 2 +-
|
||||||
|
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/tests/libtransmission/move-test.cc b/tests/libtransmission/move-test.cc
|
||||||
|
index cc5dc05ce6..e06d1daefd 100644
|
||||||
|
--- a/tests/libtransmission/move-test.cc
|
||||||
|
+++ b/tests/libtransmission/move-test.cc
|
||||||
|
@@ -84,7 +84,7 @@ TEST_P(IncompleteDirTest, incompleteDir)
|
||||||
|
|
||||||
|
auto const test_incomplete_dir_threadfunc = [](TestIncompleteDirData* data) noexcept
|
||||||
|
{
|
||||||
|
- data->session->cache->writeBlock(data->tor->id(), data->block, data->buf);
|
||||||
|
+ data->session->cache->writeBlock(data->tor->id(), data->block, std::move(data->buf));
|
||||||
|
tr_torrentGotBlock(data->tor, data->block);
|
||||||
|
data->done = true;
|
||||||
|
};
|
@ -0,0 +1,500 @@
|
|||||||
|
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
|
Loading…
Reference in new issue