fix: cache the value of tr_peerMgrGetDesiredAvailable() (#7961)

* perf: cache the value of tr_peerMgrGetDesiredAvailable()

* fix: invalidate swarm piece availability when a peer connection closes
This commit is contained in:
Charles Kerr
2025-12-25 14:32:40 -06:00
committed by GitHub
parent 850d2a277f
commit c0a8f3bbe6

View File

@@ -442,6 +442,8 @@ public:
tor_in->started_.observe([this](tr_torrent*) { on_torrent_started(); }),
tor_in->stopped_.observe([this](tr_torrent*) { on_torrent_stopped(); }),
tor_in->swarm_is_all_upload_only_.observe([this](tr_torrent* /*tor*/) { on_swarm_is_all_upload_only(); }),
tor_in->files_wanted_changed_.observe([this](tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool)
{ mark_desired_available_dirty(); }),
} }
{
rebuild_webseeds();
@@ -493,6 +495,7 @@ public:
{
peers.erase(iter);
TR_ASSERT(stats.peer_count == peerCount());
mark_swarm_piece_completeness_dirty();
}
}
@@ -560,7 +563,7 @@ public:
++stats.known_peer_from_count[from];
}
mark_all_upload_only_flag_dirty();
mark_swarm_piece_completeness_dirty();
return peer_info;
}
@@ -600,21 +603,21 @@ public:
case tr_peer_event::Type::ClientGotHave:
s->got_have.emit(s->tor, event.pieceIndex);
s->mark_all_upload_only_flag_dirty();
s->mark_swarm_piece_completeness_dirty();
break;
case tr_peer_event::Type::ClientGotHaveAll:
s->got_have_all.emit(s->tor);
s->mark_all_upload_only_flag_dirty();
s->mark_swarm_piece_completeness_dirty();
break;
case tr_peer_event::Type::ClientGotHaveNone:
s->mark_all_upload_only_flag_dirty();
s->mark_swarm_piece_completeness_dirty();
break;
case tr_peer_event::Type::ClientGotBitfield:
s->got_bitfield.emit(s->tor, msgs->has());
s->mark_all_upload_only_flag_dirty();
s->mark_swarm_piece_completeness_dirty();
break;
case tr_peer_event::Type::ClientGotChoke:
@@ -664,6 +667,16 @@ public:
}
}
[[nodiscard]] uint64_t desired_available() const
{
if (!desired_available_.has_value())
{
desired_available_ = compute_desired_available();
}
return *desired_available_;
}
libtransmission::SimpleObservable<tr_torrent*, tr_bitfield const& /*bitfield*/, tr_bitfield const& /*active requests*/>
peer_disconnect;
libtransmission::SimpleObservable<tr_torrent*, tr_bitfield const&> got_bitfield;
@@ -759,9 +772,16 @@ private:
}
}
void mark_all_upload_only_flag_dirty() noexcept
TR_CONSTEXPR20 void mark_swarm_piece_completeness_dirty() noexcept
{
pool_is_all_upload_only_.reset();
mark_desired_available_dirty();
}
TR_CONSTEXPR20 void mark_desired_available_dirty() noexcept
{
desired_available_.reset();
}
void on_torrent_doomed()
@@ -787,7 +807,7 @@ private:
peer_info->set_upload_only();
}
mark_all_upload_only_flag_dirty();
mark_swarm_piece_completeness_dirty();
}
void on_piece_completed(tr_piece_index_t piece)
@@ -962,7 +982,7 @@ private:
connectable_pool.insert_or_assign(info_this->listen_socket_address(), std::move(info_this));
EXIT:
mark_all_upload_only_flag_dirty();
mark_swarm_piece_completeness_dirty();
}
bool on_got_port_duplicate_connection(tr_peerMsgs* const msgs, std::shared_ptr<tr_peer_info> info_that)
@@ -990,14 +1010,56 @@ EXIT:
return true;
}
// count how many bytes we want that connected peers have
[[nodiscard]] uint64_t compute_desired_available() const
{
if (!tor->is_running() || tor->is_stopping() || tor->is_done() || !tor->has_metainfo())
{
return 0;
}
if (std::empty(peers))
{
return 0;
}
auto available = tr_bitfield{ tor->piece_count() };
for (auto const& peer : peers)
{
available |= peer->has();
}
if (available.has_all())
{
return tor->left_until_done();
}
auto desired_available = uint64_t{};
for (tr_piece_index_t i = 0, n = tor->piece_count(); i < n; ++i)
{
if (tor->piece_is_wanted(i) && available.test(i))
{
desired_available += tor->count_missing_bytes_in_piece(i);
}
}
TR_ASSERT(desired_available <= tor->total_size());
return desired_available;
}
// ---
// number of bad pieces a peer is allowed to send before we ban them
static auto constexpr MaxBadPiecesPerPeer = 5U;
std::array<libtransmission::ObserverTag, 8> const tags_;
std::array<libtransmission::ObserverTag, 9U> const tags_;
mutable std::optional<bool> pool_is_all_upload_only_;
// Cached value of how many bytes we want that connected peers have.
// See: `desired_available()` and `mark_desired_available_dirty()`
mutable std::optional<uint64_t> desired_available_;
};
bool tr_swarm::WishlistMediator::client_has_block(tr_block_index_t block) const
@@ -1758,47 +1820,9 @@ tr_swarm_stats tr_swarmGetStats(tr_swarm const* const swarm)
return stats;
}
/* count how many bytes we want that connected peers have */
uint64_t tr_peerMgrGetDesiredAvailable(tr_torrent const* tor)
{
TR_ASSERT(tr_isTorrent(tor));
// common shortcuts...
if (!tor->is_running() || tor->is_stopping() || tor->is_done() || !tor->has_metainfo())
{
return 0;
}
tr_swarm const* const swarm = tor->swarm;
if (swarm == nullptr || std::empty(swarm->peers))
{
return 0;
}
auto available = tr_bitfield{ tor->piece_count() };
for (auto const& peer : swarm->peers)
{
available |= peer->has();
}
if (available.has_all())
{
return tor->left_until_done();
}
auto desired_available = uint64_t{};
for (tr_piece_index_t i = 0, n = tor->piece_count(); i < n; ++i)
{
if (tor->piece_is_wanted(i) && available.test(i))
{
desired_available += tor->count_missing_bytes_in_piece(i);
}
}
TR_ASSERT(desired_available <= tor->total_size());
return desired_available;
return tor->swarm->desired_available();
}
tr_webseed_view tr_peerMgrWebseed(tr_torrent const* tor, size_t i)