refactor: use sigslot (#8309)

* fix: warning: declaration shadows a variable in the global namespace [clang-diagnostic-shadow]

* fix: warning: use 'contains' to check for membership [readability-container-contains]

* fix: warning: variable gl_confdir can be made static or moved into an anonymous namespace to enforce internal linkage [misc-use-internal-linkage]

* warning: function 'TorrentFilter::match_mode' has a definition with different parameter names [readability-inconsistent-declaration-parameter-name]

* build: add sigslot dependency

* refactor: use sigslot for tr::Blocklists

* refactor: use sigslot for torrent, peer-mgr, wishlist

* refactor: remove tr::SimpleObservable

* chore: make lint happy

warning: method 'make_wishlist' can be made static [readability-convert-member-functions-to-static]

warning: invalid case style for function 'make_wishlist' [readability-identifier-naming]

warning: do not declare C-style arrays, use std::array<> instead [cppcoreguidelines-avoid-c-arrays,hicpp-avoid-c-arrays,modernize-avoid-c-arrays]

* refactor: remove unused forward declaration of tr_peer

* chore: remove slop

* refactor: Blocklist::observe_changes() now returns a scoped connection

* build: use transmission/sigslot fork

* refactor: copyediting

* refactor: fix cyclical dependency loop between Wishlist and tr_peerMgr::WishlistMediator
This commit is contained in:
Charles Kerr
2026-02-02 22:42:28 -06:00
committed by GitHub
parent 2d740b8d8c
commit 64a53a8219
13 changed files with 505 additions and 676 deletions

3
.gitmodules vendored
View File

@@ -64,3 +64,6 @@
[submodule "third-party/crc32c"]
path = third-party/crc32c
url = https://github.com/google/crc32c.git
[submodule "third-party/sigslot"]
path = third-party/sigslot
url = https://github.com/transmission/sigslot

View File

@@ -4346,6 +4346,7 @@
"third-party/dht",
"third-party/fast_float/include",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/rapidjson/include",
"third-party/libb64/include",
@@ -4389,6 +4390,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
);
WRAPPER_EXTENSION = app;
@@ -4407,6 +4409,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -4429,6 +4432,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -4447,6 +4451,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -4592,6 +4597,7 @@
"third-party/dht",
"third-party/fast_float/include",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/rapidjson/include",
"third-party/libb64/include",
@@ -4624,6 +4630,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -4653,6 +4660,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
);
WRAPPER_EXTENSION = app;
@@ -4859,6 +4867,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
);
WRAPPER_EXTENSION = app;
@@ -4877,6 +4886,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -4918,6 +4928,7 @@
"third-party/dht",
"third-party/fast_float/include",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/rapidjson/include",
"third-party/libb64/include",
@@ -4954,6 +4965,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -4972,6 +4984,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5046,6 +5059,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
);
WRAPPER_EXTENSION = qlgenerator;
@@ -5074,6 +5088,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
);
WRAPPER_EXTENSION = qlgenerator;
@@ -5102,6 +5117,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
);
WRAPPER_EXTENSION = qlgenerator;
@@ -5164,6 +5180,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5182,6 +5199,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5332,6 +5350,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5350,6 +5369,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5368,6 +5388,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5386,6 +5407,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5404,6 +5426,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5422,6 +5445,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5440,6 +5464,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5458,6 +5483,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5476,6 +5502,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
"third-party/libevent/include",
);
@@ -5509,6 +5536,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
);
};
@@ -5541,6 +5569,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
);
};
@@ -5573,6 +5602,7 @@
SYSTEM_HEADER_SEARCH_PATHS = (
"$(inherited)",
"third-party/fmt/include",
"third-party/sigslot/include",
"third-party/small/include",
);
};

View File

@@ -12,6 +12,8 @@ add_compile_options(
# equivalent of XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES for this directory
$<$<AND:$<BOOL:${APPLE}>,$<OR:$<CXX_COMPILER_ID:AppleClang>,$<CXX_COMPILER_ID:Clang>>,$<OR:$<COMPILE_LANGUAGE:C>,$<COMPILE_LANGUAGE:CXX>>>:-fobjc-arc>)
add_subdirectory(${CMAKE_SOURCE_DIR}/third-party/sigslot ${CMAKE_BINARY_DIR}/third-party/sigslot)
add_library(${TR_NAME} STATIC)
set(IS_APPLE_CLANG FALSE)
@@ -78,7 +80,6 @@ target_sources(${TR_NAME}
mime-types.h
net.cc
net.h
observable.h
open-files.cc
open-files.h
peer-common.h
@@ -286,6 +287,7 @@ target_link_libraries(${TR_NAME}
PUBLIC
transmission::crypto_impl
fmt::fmt-header-only
sigslot
transmission::small
libevent::event)

View File

@@ -515,7 +515,7 @@ void Blocklists::set_enabled(bool is_enabled)
blocklist.setEnabled(is_enabled);
}
changed_.emit();
changed_();
}
void Blocklists::load(std::string_view folder, bool is_enabled)
@@ -523,7 +523,7 @@ void Blocklists::load(std::string_view folder, bool is_enabled)
folder_ = folder;
blocklists_ = load_folder(folder, is_enabled);
changed_.emit();
changed_();
}
// static
@@ -590,7 +590,7 @@ size_t Blocklists::update_primary_blocklist(std::string_view const external_file
blocklists_.emplace_back(std::move(*added));
}
changed_.emit();
changed_();
return n_rules;
}

View File

@@ -18,8 +18,9 @@
#include <utility> // for std::pair
#include <vector>
#include <sigslot/signal.hpp>
#include "libtransmission/net.h" // for tr_address
#include "libtransmission/observable.h"
namespace tr
{
@@ -56,9 +57,9 @@ public:
size_t update_primary_blocklist(std::string_view external_file, bool is_enabled);
template<typename Observer>
[[nodiscard]] auto observe_changes(Observer observer)
[[nodiscard]] sigslot::scoped_connection observe_changes(Observer observer) const
{
return changed_.observe(std::move(observer));
return changed_.connect_scoped(std::move(observer));
}
private:
@@ -112,7 +113,7 @@ private:
std::string folder_;
tr::SimpleObservable<> changed_;
mutable sigslot::signal<> changed_;
[[nodiscard]] static std::vector<Blocklist> load_folder(std::string_view folder, bool is_enabled);
};

View File

@@ -1,110 +0,0 @@
// This file Copyright © Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#pragma once
#ifndef __TRANSMISSION__
#error only libtransmission should #include this header.
#endif
#include <cstddef> // for size_t
#include <functional>
#include <utility> // for std::move
#include <small/map.hpp>
#include "libtransmission/tr-assert.h"
namespace tr
{
// An RAII-based subscription to an Observable.
// Returned by SimpleObservable::observe().
// Let it go out-of-scope to cancel the subscription.
class ObserverTag
{
public:
using Callback = std::function<void()>;
ObserverTag() = default;
ObserverTag(ObserverTag&& that) noexcept
{
*this = std::forward<ObserverTag>(that);
}
ObserverTag& operator=(ObserverTag&& that) noexcept
{
on_destroy_ = std::move(that.on_destroy_);
that.on_destroy_ = nullptr;
return *this;
}
ObserverTag(ObserverTag const&) = delete;
ObserverTag& operator=(ObserverTag const&) = delete;
explicit ObserverTag(Callback on_destroy)
: on_destroy_{ std::move(on_destroy) }
{
}
~ObserverTag()
{
if (on_destroy_)
{
on_destroy_();
}
}
private:
Callback on_destroy_;
};
// A simple observer/observable implementation.
// Intentionally avoids edge cases like thread safety and
// remove-during-emit; this is meant to be as lightweight
// as possible for very basic use cases.
template<typename... Args>
class SimpleObservable // NOLINT(cppcoreguidelines-special-member-functions)
{
using Key = size_t;
public:
using Observer = std::function<void(Args...)>;
~SimpleObservable()
{
TR_ASSERT(std::empty(observers_));
}
[[nodiscard]] auto observe(Observer observer)
{
auto const key = next_key++;
observers_.emplace(key, std::move(observer));
// clang-format off: TODO: remove when we bump to clang-format >= 21
return ObserverTag{ [this, key]() { remove(key); } };
// clang-format on
}
void emit(Args... args) const
{
for (auto& [tag, observer] : observers_)
{
observer(args...);
}
}
private:
void remove(Key key)
{
[[maybe_unused]] auto const n_removed = observers_.erase(key);
TR_ASSERT(n_removed == 1U);
}
static auto inline next_key = Key{ 1U };
small::map<Key, Observer, 4U> observers_;
};
} // namespace tr

View File

@@ -4,6 +4,7 @@
// License text can be found in the licenses/ folder.
#include <algorithm> // std::adjacent_find, std::sort
#include <array>
#include <cstddef>
#include <functional>
#include <ranges>
@@ -19,8 +20,9 @@
#include "libtransmission/bitfield.h"
#include "libtransmission/crypto-utils.h" // for tr_salt_shaker
#include "libtransmission/tr-macros.h"
#include "libtransmission/tr-assert.h"
#include "libtransmission/peer-mgr-wishlist.h"
#include "libtransmission/utils.h"
namespace
{
@@ -109,6 +111,81 @@ class Wishlist::Impl
public:
explicit Impl(Mediator& mediator_in);
void on_files_wanted_changed()
{
candidate_list_upkeep();
}
void on_got_bad_piece(tr_piece_index_t const piece)
{
got_bad_piece(piece);
}
void on_got_bitfield(tr_bitfield const& bitfield)
{
inc_replication_bitfield(bitfield);
}
void on_got_block(tr_block_index_t const block)
{
client_got_block(block);
}
void on_got_choke(tr_bitfield const& requests)
{
reset_blocks_bitfield(requests);
}
void on_got_have(tr_piece_index_t const piece)
{
inc_replication_piece(piece);
}
void on_got_have_all()
{
inc_replication();
}
void on_got_reject(tr_block_index_t const block)
{
reset_block(block);
}
void on_peer_disconnect(tr_bitfield const& have, tr_bitfield const& requests)
{
peer_disconnect(have, requests);
}
void on_piece_completed(tr_piece_index_t const piece)
{
remove_piece(piece);
}
void on_priority_changed()
{
recalculate_priority();
}
void on_sent_cancel(tr_block_index_t const block)
{
reset_block(block);
}
void on_sent_request(tr_block_span_t const block_span)
{
requested_block_span(block_span);
}
void on_sequential_download_changed()
{
recalculate_salt();
}
void on_sequential_download_from_piece_changed()
{
recalculate_salt();
}
[[nodiscard]] std::vector<tr_block_span_t> next(
size_t n_wanted_blocks,
std::function<bool(tr_piece_index_t)> const& peer_has_piece);
@@ -523,49 +600,11 @@ private:
CandidateVec candidates_;
std::array<tr::ObserverTag, 15U> const tags_;
Mediator& mediator_;
};
Wishlist::Impl::Impl(Mediator& mediator_in)
: tags_{ {
// candidates
mediator_in.observe_files_wanted_changed([this](tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool)
{ candidate_list_upkeep(); }),
// replication, unrequested
mediator_in.observe_peer_disconnect([this](tr_torrent*, tr_bitfield const& b, tr_bitfield const& ar)
{ peer_disconnect(b, ar); }),
// unrequested
mediator_in.observe_got_bad_piece([this](tr_torrent*, tr_piece_index_t p) { got_bad_piece(p); }),
// replication
mediator_in.observe_got_bitfield([this](tr_torrent*, tr_bitfield const& b) { inc_replication_bitfield(b); }),
// unrequested
mediator_in.observe_got_block([this](tr_torrent*, tr_block_index_t b) { client_got_block(b); }),
// unrequested
mediator_in.observe_got_choke([this](tr_torrent*, tr_bitfield const& b) { reset_blocks_bitfield(b); }),
// replication
mediator_in.observe_got_have([this](tr_torrent*, tr_piece_index_t p) { inc_replication_piece(p); }),
// replication
mediator_in.observe_got_have_all([this](tr_torrent*) { inc_replication(); }),
// unrequested
mediator_in.observe_got_reject([this](tr_torrent*, tr_peer*, tr_block_index_t b) { reset_block(b); }),
// candidates
mediator_in.observe_piece_completed([this](tr_torrent*, tr_piece_index_t p) { remove_piece(p); }),
// priority
mediator_in.observe_priority_changed([this](tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t)
{ recalculate_priority(); }),
// unrequested
mediator_in.observe_sent_cancel([this](tr_torrent*, tr_peer*, tr_block_index_t b) { reset_block(b); }),
// unrequested
mediator_in.observe_sent_request([this](tr_torrent*, tr_peer*, tr_block_span_t bs) { requested_block_span(bs); }),
// salt
mediator_in.observe_sequential_download_changed([this](tr_torrent*, bool) { recalculate_salt(); }),
// salt
mediator_in.observe_sequential_download_from_piece_changed([this](tr_torrent*, tr_piece_index_t)
{ recalculate_salt(); }),
} }
, mediator_{ mediator_in }
: mediator_{ mediator_in }
{
candidate_list_upkeep();
}
@@ -641,6 +680,81 @@ Wishlist::Wishlist(Mediator& mediator_in)
Wishlist::~Wishlist() = default;
void Wishlist::on_files_wanted_changed()
{
impl_->on_files_wanted_changed();
}
void Wishlist::on_got_bad_piece(tr_piece_index_t const piece)
{
impl_->on_got_bad_piece(piece);
}
void Wishlist::on_got_bitfield(tr_bitfield const& bitfield)
{
impl_->on_got_bitfield(bitfield);
}
void Wishlist::on_got_block(tr_block_index_t const block)
{
impl_->on_got_block(block);
}
void Wishlist::on_got_choke(tr_bitfield const& requests)
{
impl_->on_got_choke(requests);
}
void Wishlist::on_got_have(tr_piece_index_t const piece)
{
impl_->on_got_have(piece);
}
void Wishlist::on_got_have_all()
{
impl_->on_got_have_all();
}
void Wishlist::on_got_reject(tr_block_index_t const block)
{
impl_->on_got_reject(block);
}
void Wishlist::on_peer_disconnect(tr_bitfield const& have, tr_bitfield const& requests)
{
impl_->on_peer_disconnect(have, requests);
}
void Wishlist::on_piece_completed(tr_piece_index_t const piece)
{
impl_->on_piece_completed(piece);
}
void Wishlist::on_priority_changed()
{
impl_->on_priority_changed();
}
void Wishlist::on_sent_cancel(tr_block_index_t const block)
{
impl_->on_sent_cancel(block);
}
void Wishlist::on_sent_request(tr_block_span_t const block_span)
{
impl_->on_sent_request(block_span);
}
void Wishlist::on_sequential_download_changed()
{
impl_->on_sequential_download_changed();
}
void Wishlist::on_sequential_download_from_piece_changed()
{
impl_->on_sequential_download_from_piece_changed();
}
std::vector<tr_block_span_t> Wishlist::next(
size_t const n_wanted_blocks,
std::function<bool(tr_piece_index_t)> const& peer_has_piece)

View File

@@ -16,11 +16,7 @@
#include "libtransmission/transmission.h"
#include "libtransmission/observable.h"
#include "libtransmission/utils.h"
class tr_bitfield;
struct tr_peer;
/**
* Figures out what blocks we want to request next.
@@ -39,43 +35,28 @@ public:
[[nodiscard]] virtual tr_block_span_t block_span(tr_piece_index_t piece) const = 0;
[[nodiscard]] virtual tr_piece_index_t piece_count() const = 0;
[[nodiscard]] virtual tr_priority_t priority(tr_piece_index_t piece) const = 0;
[[nodiscard]] virtual tr::ObserverTag observe_files_wanted_changed(
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool>::Observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_peer_disconnect(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&, tr_bitfield const&>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_got_bad_piece(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_got_bitfield(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_got_block(
tr::SimpleObservable<tr_torrent*, tr_block_index_t>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_got_choke(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_got_have(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_got_have_all(tr::SimpleObservable<tr_torrent*>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_got_reject(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_piece_completed(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_priority_changed(
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_sent_cancel(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_sent_request(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_span_t>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_sequential_download_changed(
tr::SimpleObservable<tr_torrent*, bool>::Observer observer) = 0;
[[nodiscard]] virtual tr::ObserverTag observe_sequential_download_from_piece_changed(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) = 0;
virtual ~Mediator() = default;
};
explicit Wishlist(Mediator& mediator_in);
~Wishlist();
void on_files_wanted_changed();
void on_got_bad_piece(tr_piece_index_t piece);
void on_got_bitfield(tr_bitfield const& bitfield);
void on_got_block(tr_block_index_t block);
void on_got_choke(tr_bitfield const& requests);
void on_got_have(tr_piece_index_t piece);
void on_got_have_all();
void on_got_reject(tr_block_index_t block);
void on_peer_disconnect(tr_bitfield const& have, tr_bitfield const& requests);
void on_piece_completed(tr_piece_index_t piece);
void on_priority_changed();
void on_sent_cancel(tr_block_index_t block);
void on_sent_request(tr_block_span_t block_span);
void on_sequential_download_changed();
void on_sequential_download_from_piece_changed();
// the next blocks that we should request from a peer
[[nodiscard]] std::vector<tr_block_span_t> next(
size_t n_wanted_blocks,

View File

@@ -23,6 +23,8 @@
#include <small/map.hpp>
#include <small/vector.hpp>
#include <sigslot/signal.hpp>
#include <fmt/format.h>
#define LIBTRANSMISSION_PEER_MODULE
@@ -36,7 +38,6 @@
#include "libtransmission/interned-string.h"
#include "libtransmission/log.h"
#include "libtransmission/net.h"
#include "libtransmission/observable.h"
#include "libtransmission/peer-common.h"
#include "libtransmission/peer-io.h"
#include "libtransmission/peer-mgr-wishlist.h"
@@ -366,59 +367,108 @@ public:
using Peers = std::vector<std::shared_ptr<tr_peerMsgs>>;
using Pool = small::map<tr_socket_address, std::shared_ptr<tr_peer_info>>;
class WishlistMediator final : public Wishlist::Mediator
class WishlistController final : public Wishlist::Mediator
{
public:
explicit WishlistMediator(tr_swarm& swarm)
explicit WishlistController(tr_swarm& swarm)
: tor_{ *swarm.tor }
, swarm_{ swarm }
, wishlist_{ *this }
, signal_tags_{ {
tor_.files_wanted_changed_.connect_scoped([this](tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool)
{ wishlist_.on_files_wanted_changed(); }),
swarm_.peer_disconnect.connect_scoped(
[this](tr_torrent*, tr_bitfield const& have, tr_bitfield const& requests)
{ wishlist_.on_peer_disconnect(have, requests); }),
tor_.got_bad_piece_.connect_scoped([this](tr_torrent*, tr_piece_index_t piece)
{ wishlist_.on_got_bad_piece(piece); }),
swarm_.got_bitfield.connect_scoped([this](tr_torrent*, tr_bitfield const& bitfield)
{ wishlist_.on_got_bitfield(bitfield); }),
swarm_.got_block.connect_scoped([this](tr_torrent*, tr_block_index_t block)
{ wishlist_.on_got_block(block); }),
swarm_.got_choke.connect_scoped([this](tr_torrent*, tr_bitfield const& bitfield)
{ wishlist_.on_got_choke(bitfield); }),
swarm_.got_have.connect_scoped([this](tr_torrent*, tr_piece_index_t piece) { wishlist_.on_got_have(piece); }),
swarm_.got_have_all.connect_scoped([this](tr_torrent*) { wishlist_.on_got_have_all(); }),
swarm_.got_reject.connect_scoped([this](tr_torrent*, tr_peer*, tr_block_index_t block)
{ wishlist_.on_got_reject(block); }),
tor_.piece_completed_.connect_scoped([this](tr_torrent*, tr_piece_index_t piece)
{ wishlist_.on_piece_completed(piece); }),
tor_.priority_changed_.connect_scoped(
[this](tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t)
{ wishlist_.on_priority_changed(); }),
swarm_.sent_cancel.connect_scoped([this](tr_torrent*, tr_peer*, tr_block_index_t block)
{ wishlist_.on_sent_cancel(block); }),
swarm_.sent_request.connect_scoped([this](tr_torrent*, tr_peer*, tr_block_span_t block_span)
{ wishlist_.on_sent_request(block_span); }),
tor_.sequential_download_changed_.connect_scoped([this](tr_torrent*, bool)
{ wishlist_.on_sequential_download_changed(); }),
tor_.sequential_download_from_piece_changed_.connect_scoped(
[this](tr_torrent*, tr_piece_index_t) { wishlist_.on_sequential_download_from_piece_changed(); }),
} }
{
}
[[nodiscard]] bool client_has_block(tr_block_index_t block) const override;
[[nodiscard]] bool client_has_piece(tr_piece_index_t piece) const override;
[[nodiscard]] bool client_wants_piece(tr_piece_index_t piece) const override;
[[nodiscard]] bool is_sequential_download() const override;
[[nodiscard]] tr_piece_index_t sequential_download_from_piece() const override;
[[nodiscard]] size_t count_piece_replication(tr_piece_index_t piece) const override;
[[nodiscard]] tr_block_span_t block_span(tr_piece_index_t piece) const override;
[[nodiscard]] tr_piece_index_t piece_count() const override;
[[nodiscard]] tr_priority_t priority(tr_piece_index_t piece) const override;
[[nodiscard]] auto next(size_t const n_wanted_blocks, std::function<bool(tr_piece_index_t)> const& peer_has_piece)
{
return wishlist_.next(n_wanted_blocks, peer_has_piece);
}
[[nodiscard]] tr::ObserverTag observe_files_wanted_changed(
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_peer_disconnect(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&, tr_bitfield const&>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_got_bad_piece(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_got_bitfield(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_got_block(
tr::SimpleObservable<tr_torrent*, tr_block_index_t>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_got_choke(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_got_have(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_got_have_all(tr::SimpleObservable<tr_torrent*>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_got_reject(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_piece_completed(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_priority_changed(
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t>::Observer observer)
override;
[[nodiscard]] tr::ObserverTag observe_sent_cancel(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_sent_request(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_span_t>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_sequential_download_changed(
tr::SimpleObservable<tr_torrent*, bool>::Observer observer) override;
[[nodiscard]] tr::ObserverTag observe_sequential_download_from_piece_changed(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) override;
[[nodiscard]] bool client_has_block(tr_block_index_t const block) const override
{
return tor_.has_block(block);
}
[[nodiscard]] bool client_has_piece(tr_piece_index_t const piece) const override
{
return tor_.has_blocks(block_span(piece));
}
[[nodiscard]] bool client_wants_piece(tr_piece_index_t const piece) const override
{
return tor_.piece_is_wanted(piece);
}
[[nodiscard]] bool is_sequential_download() const override
{
return tor_.is_sequential_download();
}
[[nodiscard]] tr_piece_index_t sequential_download_from_piece() const override
{
return tor_.sequential_download_from_piece();
}
[[nodiscard]] size_t count_piece_replication(tr_piece_index_t const piece) const override
{
auto const op = [piece](size_t acc, auto const& peer)
{
return acc + (peer->has_piece(piece) ? 1U : 0U);
};
return std::accumulate(std::begin(swarm_.peers), std::end(swarm_.peers), size_t{}, op) +
std::accumulate(std::begin(swarm_.webseeds), std::end(swarm_.webseeds), size_t{}, op);
}
[[nodiscard]] tr_block_span_t block_span(tr_piece_index_t const piece) const override
{
return tor_.block_span_for_piece(piece);
}
[[nodiscard]] tr_piece_index_t piece_count() const override
{
return tor_.piece_count();
}
[[nodiscard]] tr_priority_t priority(tr_piece_index_t const piece) const override
{
return tor_.piece_priority(piece);
}
private:
tr_torrent& tor_;
tr_swarm& swarm_;
Wishlist wishlist_;
std::array<sigslot::scoped_connection, 15U> signal_tags_; // depends-on: wishlist_
};
[[nodiscard]] auto unique_lock() const
@@ -430,14 +480,14 @@ public:
: manager{ manager_in }
, tor{ tor_in }
, tags_{ {
tor_in->done_.observe([this](tr_torrent*, bool) { on_torrent_done(); }),
tor_in->doomed_.observe([this](tr_torrent*) { on_torrent_doomed(); }),
tor_in->got_bad_piece_.observe([this](tr_torrent*, tr_piece_index_t p) { on_got_bad_piece(p); }),
tor_in->got_metainfo_.observe([this](tr_torrent*) { on_got_metainfo(); }),
tor_in->piece_completed_.observe([this](tr_torrent*, tr_piece_index_t p) { on_piece_completed(p); }),
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->done_.connect_scoped([this](tr_torrent*, bool) { on_torrent_done(); }),
tor_in->doomed_.connect_scoped([this](tr_torrent*) { on_torrent_doomed(); }),
tor_in->got_bad_piece_.connect_scoped([this](tr_torrent*, tr_piece_index_t p) { on_got_bad_piece(p); }),
tor_in->got_metainfo_.connect_scoped([this](tr_torrent*) { on_got_metainfo(); }),
tor_in->piece_completed_.connect_scoped([this](tr_torrent*, tr_piece_index_t p) { on_piece_completed(p); }),
tor_in->started_.connect_scoped([this](tr_torrent*) { on_torrent_started(); }),
tor_in->stopped_.connect_scoped([this](tr_torrent*) { on_torrent_stopped(); }),
tor_in->swarm_is_all_upload_only_.connect_scoped([this](tr_torrent* /*tor*/) { on_swarm_is_all_upload_only(); }),
} }
{
rebuild_webseeds();
@@ -450,6 +500,8 @@ public:
~tr_swarm()
{
wishlist_controller.reset();
auto const lock = unique_lock();
TR_ASSERT(!is_running);
TR_ASSERT(std::empty(peers));
@@ -477,7 +529,7 @@ public:
{
auto const lock = unique_lock();
peer_disconnect.emit(tor, peer->has(), peer->active_requests);
peer_disconnect(tor, peer->has(), peer->active_requests);
auto const& peer_info = peer->peer_info;
TR_ASSERT(peer_info);
@@ -573,7 +625,7 @@ public:
{
auto* const tor = s->tor;
auto const loc = tor->piece_loc(event.pieceIndex, event.offset);
s->sent_cancel.emit(tor, msgs, loc.block);
s->sent_cancel(tor, msgs, loc.block);
}
break;
@@ -594,12 +646,12 @@ public:
break;
case tr_peer_event::Type::ClientGotHave:
s->got_have.emit(s->tor, event.pieceIndex);
s->got_have(s->tor, event.pieceIndex);
s->mark_all_upload_only_flag_dirty();
break;
case tr_peer_event::Type::ClientGotHaveAll:
s->got_have_all.emit(s->tor);
s->got_have_all(s->tor);
s->mark_all_upload_only_flag_dirty();
break;
@@ -608,12 +660,12 @@ public:
break;
case tr_peer_event::Type::ClientGotBitfield:
s->got_bitfield.emit(s->tor, msgs->has());
s->got_bitfield(s->tor, msgs->has());
s->mark_all_upload_only_flag_dirty();
break;
case tr_peer_event::Type::ClientGotChoke:
s->got_choke.emit(s->tor, msgs->active_requests);
s->got_choke(s->tor, msgs->active_requests);
break;
case tr_peer_event::Type::ClientGotPort:
@@ -659,15 +711,15 @@ public:
}
}
tr::SimpleObservable<tr_torrent*, tr_bitfield const& /*bitfield*/, tr_bitfield const& /*active requests*/> peer_disconnect;
tr::SimpleObservable<tr_torrent*, tr_bitfield const&> got_bitfield;
tr::SimpleObservable<tr_torrent*, tr_block_index_t> got_block;
tr::SimpleObservable<tr_torrent*, tr_bitfield const&> got_choke;
tr::SimpleObservable<tr_torrent*, tr_piece_index_t> got_have;
tr::SimpleObservable<tr_torrent*> got_have_all;
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t> got_reject;
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t> sent_cancel;
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_span_t> sent_request;
sigslot::signal<tr_torrent*, tr_bitfield const& /*bitfield*/, tr_bitfield const& /*active requests*/> peer_disconnect;
sigslot::signal<tr_torrent*, tr_bitfield const&> got_bitfield;
sigslot::signal<tr_torrent*, tr_block_index_t> got_block;
sigslot::signal<tr_torrent*, tr_bitfield const&> got_choke;
sigslot::signal<tr_torrent*, tr_piece_index_t> got_have;
sigslot::signal<tr_torrent*> got_have_all;
sigslot::signal<tr_torrent*, tr_peer*, tr_block_index_t> got_reject;
sigslot::signal<tr_torrent*, tr_peer*, tr_block_index_t> sent_cancel;
sigslot::signal<tr_torrent*, tr_peer*, tr_block_span_t> sent_request;
mutable tr_swarm_stats stats = {};
@@ -684,8 +736,7 @@ public:
Peers peers;
// depends-on: tor
WishlistMediator wishlist_mediator{ *this };
std::unique_ptr<Wishlist> wishlist;
std::unique_ptr<WishlistController> wishlist_controller;
Pool connectable_pool;
@@ -731,7 +782,7 @@ private:
is_running = false;
remove_all_peers();
wishlist.reset();
wishlist_controller.reset();
for (auto& [sockaddr, peer_info] : connectable_pool)
{
peer_info->destroy_handshake();
@@ -770,7 +821,7 @@ private:
void on_torrent_done()
{
std::ranges::for_each(peers, [](auto const& peer) { peer->set_interested(false); });
wishlist.reset();
wishlist_controller.reset();
}
void on_swarm_is_all_upload_only()
@@ -881,7 +932,8 @@ private:
auto* const tor = s->tor;
auto const loc_begin = tor->piece_loc(event.pieceIndex, event.offset);
auto const loc_end = tor->piece_loc(event.pieceIndex, event.offset, event.length);
s->sent_request.emit(tor, peer, { .begin = loc_begin.block, .end = loc_end.block });
auto const span = tr_block_span_t{ .begin = loc_begin.block, .end = loc_end.block };
s->sent_request(tor, peer, span);
}
break;
@@ -889,7 +941,7 @@ private:
{
auto* const tor = s->tor;
auto const loc = tor->piece_loc(event.pieceIndex, event.offset);
s->got_reject.emit(tor, peer, loc.block);
s->got_reject(tor, peer, loc.block);
}
break;
@@ -900,7 +952,7 @@ private:
s->cancel_all_requests_for_block(loc.block, peer);
peer->blocks_sent_to_client.add(tr_time(), 1);
peer->blame.set(loc.piece);
s->got_block.emit(tor, loc.block); // put this line before calling tr_torrent callback
s->got_block(tor, loc.block); // put this line before calling tr_torrent callback
tor->on_block_received(loc.block);
}
break;
@@ -989,150 +1041,11 @@ EXIT:
// number of bad pieces a peer is allowed to send before we ban them
static auto constexpr MaxBadPiecesPerPeer = 5U;
std::array<tr::ObserverTag, 8> const tags_;
std::array<sigslot::scoped_connection, 8> const tags_;
mutable std::optional<bool> pool_is_all_upload_only_;
};
bool tr_swarm::WishlistMediator::client_has_block(tr_block_index_t block) const
{
return tor_.has_block(block);
}
bool tr_swarm::WishlistMediator::client_has_piece(tr_piece_index_t piece) const
{
return tor_.has_blocks(block_span(piece));
}
bool tr_swarm::WishlistMediator::client_wants_piece(tr_piece_index_t piece) const
{
return tor_.piece_is_wanted(piece);
}
bool tr_swarm::WishlistMediator::is_sequential_download() const
{
return tor_.is_sequential_download();
}
tr_piece_index_t tr_swarm::WishlistMediator::sequential_download_from_piece() const
{
return tor_.sequential_download_from_piece();
}
size_t tr_swarm::WishlistMediator::count_piece_replication(tr_piece_index_t piece) const
{
auto const op = [piece](size_t acc, auto const& peer)
{
return acc + (peer->has_piece(piece) ? 1U : 0U);
};
return std::accumulate(std::begin(swarm_.peers), std::end(swarm_.peers), size_t{}, op) +
std::accumulate(std::begin(swarm_.webseeds), std::end(swarm_.webseeds), size_t{}, op);
}
tr_block_span_t tr_swarm::WishlistMediator::block_span(tr_piece_index_t piece) const
{
return tor_.block_span_for_piece(piece);
}
tr_piece_index_t tr_swarm::WishlistMediator::piece_count() const
{
return tor_.piece_count();
}
tr_priority_t tr_swarm::WishlistMediator::priority(tr_piece_index_t piece) const
{
return tor_.piece_priority(piece);
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_files_wanted_changed(
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool>::Observer observer)
{
return tor_.files_wanted_changed_.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_peer_disconnect(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&, tr_bitfield const&>::Observer observer)
{
return swarm_.peer_disconnect.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_got_bad_piece(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer)
{
return tor_.got_bad_piece_.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_got_bitfield(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&>::Observer observer)
{
return swarm_.got_bitfield.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_got_block(
tr::SimpleObservable<tr_torrent*, tr_block_index_t>::Observer observer)
{
return swarm_.got_block.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_got_choke(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&>::Observer observer)
{
return swarm_.got_choke.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_got_have(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer)
{
return swarm_.got_have.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_got_have_all(tr::SimpleObservable<tr_torrent*>::Observer observer)
{
return swarm_.got_have_all.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_got_reject(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t>::Observer observer)
{
return swarm_.got_reject.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_piece_completed(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer)
{
return tor_.piece_completed_.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_priority_changed(
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t>::Observer observer)
{
return tor_.priority_changed_.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_sent_cancel(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t>::Observer observer)
{
return swarm_.sent_cancel.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_sent_request(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_span_t>::Observer observer)
{
return swarm_.sent_request.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_sequential_download_changed(
tr::SimpleObservable<tr_torrent*, bool>::Observer observer)
{
return tor_.sequential_download_changed_.observe(std::move(observer));
}
tr::ObserverTag tr_swarm::WishlistMediator::observe_sequential_download_from_piece_changed(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer)
{
return tor_.sequential_download_from_piece_changed_.observe(std::move(observer));
}
// ---
struct tr_peerMgr
@@ -1163,7 +1076,11 @@ public:
using OutboundCandidates = small::
max_size_vector<std::pair<tr_torrent_id_t, tr_socket_address>, OutboundCandidateListCapacity>;
explicit tr_peerMgr(tr_session* session_in, tr::TimerMaker& timer_maker, tr_torrents& torrents, tr::Blocklists& blocklist)
explicit tr_peerMgr(
tr_session* session_in,
tr::TimerMaker& timer_maker,
tr_torrents& torrents,
tr::Blocklists const& blocklist)
: session{ session_in }
, torrents_{ torrents }
, blocklists_{ blocklist }
@@ -1254,7 +1171,7 @@ private:
std::unique_ptr<tr::Timer> const peer_info_timer_;
std::unique_ptr<tr::Timer> const rechoke_timer_;
tr::ObserverTag const blocklists_tag_;
sigslot::scoped_connection blocklists_tag_;
};
// --- tr_peer virtual functions
@@ -1299,13 +1216,13 @@ void tr_peerMgrFree(tr_peerMgr* manager)
std::vector<tr_block_span_t> tr_peerMgrGetNextRequests(tr_torrent* torrent, tr_peer const* peer, size_t numwant)
{
TR_ASSERT(!torrent->is_done());
tr_swarm const& swarm = *torrent->swarm;
TR_ASSERT(swarm.wishlist);
if (!swarm.wishlist)
if (auto& controller = torrent->swarm->wishlist_controller)
{
return {};
return controller->next(numwant, [peer](tr_piece_index_t p) { return peer->has_piece(p); });
}
return swarm.wishlist->next(numwant, [peer](tr_piece_index_t p) { return peer->has_piece(p); });
return {};
}
namespace
@@ -1688,7 +1605,7 @@ void tr_swarm::on_torrent_started()
auto const lock = unique_lock();
is_running = true;
manager->rechokeSoon();
wishlist = std::make_unique<Wishlist>(wishlist_mediator);
wishlist_controller = std::make_unique<WishlistController>(*this);
}
void tr_swarm::on_torrent_stopped()

View File

@@ -541,7 +541,7 @@ void freeTorrent(tr_torrent* tor)
tr_session* session = tor->session;
tor->doomed_.emit(tor);
tor->doomed_(tor);
session->announcer_->removeTorrent(tor);
@@ -643,7 +643,7 @@ void tr_torrent::start_in_session_thread()
session->announcer_->startTorrent(this);
lpdAnnounceAt = now;
started_.emit(this);
started_(this);
}
void tr_torrent::stop_now()
@@ -666,7 +666,7 @@ void tr_torrent::stop_now()
session->verify_remove(this);
stopped_.emit(this);
stopped_(this);
session->announcer_->stopTorrent(this);
session->close_torrent_files(id());
@@ -1022,7 +1022,7 @@ void tr_torrent::set_metainfo(tr_torrent_metainfo tm)
metainfo_ = std::move(tm);
on_metainfo_updated();
got_metainfo_.emit(this);
got_metainfo_(this);
session->onMetadataCompleted(this);
set_dirty();
mark_edited();
@@ -1826,7 +1826,7 @@ void tr_torrent::recheck_completeness()
set_location(download_dir(), true, nullptr);
}
done_.emit(this, recent_change);
done_(this, recent_change);
}
session->onTorrentCompletenessChanged(this, completeness_, was_running);
@@ -1960,7 +1960,7 @@ void tr_torrent::set_file_priorities(tr_file_index_t const* files, tr_file_index
[this, priority](tr_file_index_t file) { return priority != file_priorities_.file_priority(file); }))
{
file_priorities_.set(files, file_count, priority);
priority_changed_.emit(this, files, file_count, priority);
priority_changed_(this, files, file_count, priority);
set_dirty();
mark_changed();
}
@@ -2052,7 +2052,7 @@ void tr_torrent::on_tracker_response(tr_tracker_event const* event)
case tr_tracker_event::Type::Counts:
if (is_private() && (event->leechers == 0 || event->downloaders == 0))
{
swarm_is_all_upload_only_.emit(this);
swarm_is_all_upload_only_(this);
}
break;
@@ -2163,7 +2163,7 @@ void tr_torrent::on_file_completed(tr_file_index_t const file)
void tr_torrent::on_piece_completed(tr_piece_index_t const piece)
{
piece_completed_.emit(this, piece);
piece_completed_(this, piece);
// bookkeeping
set_needs_completeness_check();
@@ -2192,7 +2192,7 @@ void tr_torrent::on_piece_failed(tr_piece_index_t const piece)
auto const n = piece_size(piece);
bytes_corrupt_ += n;
bytes_downloaded_.reduce(n);
got_bad_piece_.emit(this, piece);
got_bad_piece_(this, piece);
set_has_piece(piece, false);
}

View File

@@ -20,6 +20,8 @@
#include <utility>
#include <vector>
#include <sigslot/signal.hpp>
#include "libtransmission/transmission.h"
#include "libtransmission/announce-list.h"
@@ -31,7 +33,6 @@
#include "libtransmission/file-piece-map.h"
#include "libtransmission/interned-string.h"
#include "libtransmission/log.h"
#include "libtransmission/observable.h"
#include "libtransmission/session.h"
#include "libtransmission/torrent-files.h"
#include "libtransmission/torrent-magnet.h"
@@ -438,7 +439,7 @@ struct tr_torrent
if (priority != file_priorities_.file_priority(file))
{
file_priorities_.set(file, priority);
priority_changed_.emit(this, &file, 1U, priority);
priority_changed_(this, &file, 1U, priority);
set_dirty();
mark_changed();
}
@@ -768,7 +769,7 @@ struct tr_torrent
session->flush_torrent_files(id());
}
sequential_download_ = is_sequential;
sequential_download_changed_.emit(this, is_sequential);
sequential_download_changed_(this, is_sequential);
set_dirty();
}
}
@@ -784,7 +785,7 @@ struct tr_torrent
if (is_valid && piece != sequential_download_from_piece_)
{
sequential_download_from_piece_ = piece;
sequential_download_from_piece_changed_.emit(this, piece);
sequential_download_from_piece_changed_(this, piece);
return true;
}
return false;
@@ -1012,18 +1013,18 @@ struct tr_torrent
// ---
tr::SimpleObservable<tr_torrent*, bool /*because_downloaded_last_piece*/> done_;
tr::SimpleObservable<tr_torrent*, tr_piece_index_t> got_bad_piece_;
tr::SimpleObservable<tr_torrent*, tr_piece_index_t> piece_completed_;
tr::SimpleObservable<tr_torrent*> doomed_;
tr::SimpleObservable<tr_torrent*> got_metainfo_;
tr::SimpleObservable<tr_torrent*> started_;
tr::SimpleObservable<tr_torrent*> stopped_;
tr::SimpleObservable<tr_torrent*> swarm_is_all_upload_only_;
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool> files_wanted_changed_;
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t> priority_changed_;
tr::SimpleObservable<tr_torrent*, bool> sequential_download_changed_;
tr::SimpleObservable<tr_torrent*, tr_piece_index_t> sequential_download_from_piece_changed_;
sigslot::signal<tr_torrent*, bool /*because_downloaded_last_piece*/> done_;
sigslot::signal<tr_torrent*, tr_piece_index_t> got_bad_piece_;
sigslot::signal<tr_torrent*, tr_piece_index_t> piece_completed_;
sigslot::signal<tr_torrent*> doomed_;
sigslot::signal<tr_torrent*> got_metainfo_;
sigslot::signal<tr_torrent*> started_;
sigslot::signal<tr_torrent*> stopped_;
sigslot::signal<tr_torrent*> swarm_is_all_upload_only_;
sigslot::signal<tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool> files_wanted_changed_;
sigslot::signal<tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t> priority_changed_;
sigslot::signal<tr_torrent*, bool> sequential_download_changed_;
sigslot::signal<tr_torrent*, tr_piece_index_t> sequential_download_from_piece_changed_;
CumulativeCount bytes_corrupt_;
CumulativeCount bytes_downloaded_;
@@ -1260,7 +1261,7 @@ private:
files_wanted_.set(files, n_files, wanted);
completion_.invalidate_size_when_done();
files_wanted_changed_.emit(this, files, n_files, wanted);
files_wanted_changed_(this, files, n_files, wanted);
if (!is_bootstrapping)
{

View File

@@ -3,9 +3,9 @@
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include <array>
#include <cstddef> // size_t
#include <map>
#include <memory>
#include <set>
#define LIBTRANSMISSION_PEER_MODULE
@@ -33,12 +33,7 @@ protected:
bool is_sequential_download_ = false;
tr_piece_index_t sequential_download_from_piece_ = 0;
PeerMgrWishlistTest& parent_;
explicit MockMediator(PeerMgrWishlistTest& parent)
: parent_{ parent }
{
}
MockMediator() = default;
[[nodiscard]] bool client_has_block(tr_block_index_t block) const override
{
@@ -84,114 +79,8 @@ protected:
{
return piece_priority_[piece];
}
[[nodiscard]] tr::ObserverTag observe_files_wanted_changed(
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool>::Observer observer) override
{
return parent_.files_wanted_changed_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_peer_disconnect(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&, tr_bitfield const&>::Observer observer) override
{
return parent_.peer_disconnect_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_got_bad_piece(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) override
{
return parent_.got_bad_piece_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_got_bitfield(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&>::Observer observer) override
{
return parent_.got_bitfield_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_got_block(
tr::SimpleObservable<tr_torrent*, tr_block_index_t>::Observer observer) override
{
return parent_.got_block_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_got_choke(
tr::SimpleObservable<tr_torrent*, tr_bitfield const&>::Observer observer) override
{
return parent_.got_choke_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_got_have(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) override
{
return parent_.got_have_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_got_have_all(tr::SimpleObservable<tr_torrent*>::Observer observer) override
{
return parent_.got_have_all_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_got_reject(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t>::Observer observer) override
{
return parent_.got_reject_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_piece_completed(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) override
{
return parent_.piece_completed_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_priority_changed(
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t>::Observer observer)
override
{
return parent_.priority_changed_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_sent_cancel(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t>::Observer observer) override
{
return parent_.sent_cancel_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_sent_request(
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_span_t>::Observer observer) override
{
return parent_.sent_request_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_sequential_download_changed(
tr::SimpleObservable<tr_torrent*, bool>::Observer observer) override
{
return parent_.sequential_download_changed_.observe(std::move(observer));
}
[[nodiscard]] tr::ObserverTag observe_sequential_download_from_piece_changed(
tr::SimpleObservable<tr_torrent*, tr_piece_index_t>::Observer observer) override
{
return parent_.sequential_download_from_piece_changed_.observe(std::move(observer));
}
};
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, bool> files_wanted_changed_;
tr::SimpleObservable<tr_torrent*, tr_bitfield const&, tr_bitfield const&> peer_disconnect_;
tr::SimpleObservable<tr_torrent*, tr_piece_index_t> got_bad_piece_;
tr::SimpleObservable<tr_torrent*, tr_bitfield const&> got_bitfield_;
tr::SimpleObservable<tr_torrent*, tr_block_index_t> got_block_;
tr::SimpleObservable<tr_torrent*, tr_bitfield const&> got_choke_;
tr::SimpleObservable<tr_torrent*, tr_piece_index_t> got_have_;
tr::SimpleObservable<tr_torrent*> got_have_all_;
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t> got_reject_;
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_index_t> sent_cancel_;
tr::SimpleObservable<tr_torrent*, tr_peer*, tr_block_span_t> sent_request_;
tr::SimpleObservable<tr_torrent*, tr_piece_index_t> piece_completed_;
tr::SimpleObservable<tr_torrent*, tr_file_index_t const*, tr_file_index_t, tr_priority_t> priority_changed_;
tr::SimpleObservable<tr_torrent*, bool> sequential_download_changed_;
tr::SimpleObservable<tr_torrent*, tr_piece_index_t> sequential_download_from_piece_changed_;
static auto constexpr PeerHasAllPieces = [](tr_piece_index_t)
{
return true;
@@ -200,7 +89,7 @@ protected:
TEST_F(PeerMgrWishlistTest, doesNotRequestPiecesThatAreNotWanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -224,7 +113,7 @@ TEST_F(PeerMgrWishlistTest, doesNotRequestPiecesThatAreNotWanted)
TEST_F(PeerMgrWishlistTest, doesNotRequestPiecesThatClientHas)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -254,7 +143,7 @@ TEST_F(PeerMgrWishlistTest, doesNotRequestPiecesThatClientHas)
TEST_F(PeerMgrWishlistTest, onlyRequestBlocksThePeerHas)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -294,7 +183,7 @@ TEST_F(PeerMgrWishlistTest, onlyRequestBlocksThePeerHas)
TEST_F(PeerMgrWishlistTest, doesNotRequestSameBlockTwice)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -316,7 +205,7 @@ TEST_F(PeerMgrWishlistTest, doesNotRequestSameBlockTwice)
// but we've already requested blocks [0..10) from this peer,
// so we don't want to send repeated requests
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 10 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 10 });
// even if we ask wishlist for all the blocks,
// it should omit blocks [0..10) from the return set
@@ -333,9 +222,9 @@ TEST_F(PeerMgrWishlistTest, doesNotRequestSameBlockTwice)
TEST_F(PeerMgrWishlistTest, sequentialDownload)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -397,9 +286,9 @@ TEST_F(PeerMgrWishlistTest, sequentialDownload)
TEST_F(PeerMgrWishlistTest, sequentialDownloadFromPiece)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: four pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -452,7 +341,7 @@ TEST_F(PeerMgrWishlistTest, sequentialDownloadFromPiece)
TEST_F(PeerMgrWishlistTest, doesNotRequestTooManyBlocks)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -484,9 +373,9 @@ TEST_F(PeerMgrWishlistTest, doesNotRequestTooManyBlocks)
TEST_F(PeerMgrWishlistTest, prefersHighPriorityPieces)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -533,9 +422,9 @@ TEST_F(PeerMgrWishlistTest, prefersHighPriorityPieces)
TEST_F(PeerMgrWishlistTest, prefersNearlyCompletePieces)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, same size
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -609,9 +498,9 @@ TEST_F(PeerMgrWishlistTest, prefersNearlyCompletePieces)
TEST_F(PeerMgrWishlistTest, prefersRarerPieces)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -671,9 +560,9 @@ TEST_F(PeerMgrWishlistTest, prefersRarerPieces)
TEST_F(PeerMgrWishlistTest, peerDisconnectDecrementsReplication)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -698,7 +587,7 @@ TEST_F(PeerMgrWishlistTest, peerDisconnectDecrementsReplication)
// first piece should be the rarest piece according to the cache
auto have = tr_bitfield{ 3 };
have.set(0);
peer_disconnect_.emit(nullptr, have, tr_bitfield{ 300 });
wishlist.on_peer_disconnect(have, tr_bitfield{ 300 });
// this is what a real mediator should return at this point:
// mediator.piece_replication_[0] = 1;
@@ -744,9 +633,9 @@ TEST_F(PeerMgrWishlistTest, peerDisconnectDecrementsReplication)
TEST_F(PeerMgrWishlistTest, gotBadPieceResetsPiece)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -768,16 +657,16 @@ TEST_F(PeerMgrWishlistTest, gotBadPieceResetsPiece)
auto wishlist = Wishlist{ mediator };
// we already requested 50 blocks each from every piece
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 50 });
sent_request_.emit(nullptr, nullptr, { .begin = 100, .end = 150 });
sent_request_.emit(nullptr, nullptr, { .begin = 200, .end = 250 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 50 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 100, .end = 150 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 200, .end = 250 });
// we request the rest of a random piece
auto const random_piece = tr_rand_int(3U);
sent_request_.emit(nullptr, nullptr, { .begin = (random_piece * 100U) + 50U, .end = (random_piece + 1U) * 100U });
wishlist.on_sent_request(tr_block_span_t{ .begin = (random_piece * 100U) + 50U, .end = (random_piece + 1U) * 100U });
// the random piece turns out to be corrupted, so all blocks should be missing again
got_bad_piece_.emit(nullptr, random_piece);
wishlist.on_got_bad_piece(random_piece);
return std::pair{ wishlist.next(n_wanted, PeerHasAllPieces), random_piece };
};
@@ -803,9 +692,9 @@ TEST_F(PeerMgrWishlistTest, gotBadPieceResetsPiece)
TEST_F(PeerMgrWishlistTest, gotBitfieldIncrementsReplication)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -830,7 +719,7 @@ TEST_F(PeerMgrWishlistTest, gotBitfieldIncrementsReplication)
// third piece should be the rarest piece according to the cache
auto have = tr_bitfield{ 3 };
have.set_span(0, 2);
got_bitfield_.emit(nullptr, have);
wishlist.on_got_bitfield(have);
// this is what a real mediator should return at this point:
// mediator.piece_replication_[0] = 3;
@@ -877,9 +766,9 @@ TEST_F(PeerMgrWishlistTest, gotBitfieldIncrementsReplication)
TEST_F(PeerMgrWishlistTest, sentRequestsResortsPiece)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -902,7 +791,7 @@ TEST_F(PeerMgrWishlistTest, sentRequestsResortsPiece)
// we requested block 0 from someone, the wishlist should resort the
// candidate list cache
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 1 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 1 });
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -945,9 +834,9 @@ TEST_F(PeerMgrWishlistTest, sentRequestsResortsPiece)
TEST_F(PeerMgrWishlistTest, gotBlockResortsPiece)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -970,7 +859,7 @@ TEST_F(PeerMgrWishlistTest, gotBlockResortsPiece)
// we received block 0 from someone, the wishlist should resort the
// candidate list cache
got_block_.emit(nullptr, 0);
wishlist.on_got_block(0);
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -1013,9 +902,9 @@ TEST_F(PeerMgrWishlistTest, gotBlockResortsPiece)
TEST_F(PeerMgrWishlistTest, gotHaveIncrementsReplication)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1038,7 +927,7 @@ TEST_F(PeerMgrWishlistTest, gotHaveIncrementsReplication)
// a peer sent a "Have" message for the first piece, now the
// first piece should be the least rare piece according to the cache
got_have_.emit(nullptr, 0);
wishlist.on_got_have(0);
// this is what a real mediator should return at this point:
// mediator.piece_replication_[0] = 3;
@@ -1084,9 +973,9 @@ TEST_F(PeerMgrWishlistTest, gotHaveIncrementsReplication)
TEST_F(PeerMgrWishlistTest, gotChokeResetsRequestedBlocks)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1108,12 +997,12 @@ TEST_F(PeerMgrWishlistTest, gotChokeResetsRequestedBlocks)
auto wishlist = Wishlist{ mediator };
// we have active requests to the first 250 blocks
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 250 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 250 });
// a peer sent a "Choke" message, which cancels some active requests
tr_bitfield requested{ 300 };
requested.set_span(0, 10);
got_choke_.emit(nullptr, requested);
wishlist.on_got_choke(requested);
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -1141,9 +1030,9 @@ TEST_F(PeerMgrWishlistTest, gotChokeResetsRequestedBlocks)
TEST_F(PeerMgrWishlistTest, gotHaveAllDoesNotAffectOrder)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1165,7 +1054,7 @@ TEST_F(PeerMgrWishlistTest, gotHaveAllDoesNotAffectOrder)
auto wishlist = Wishlist{ mediator };
// a peer sent a "Have All" message, this should not affect the piece order
got_have_all_.emit(nullptr);
wishlist.on_got_have_all();
// this is what a real mediator should return at this point:
// mediator.piece_replication_[0] = 2;
@@ -1212,9 +1101,9 @@ TEST_F(PeerMgrWishlistTest, gotHaveAllDoesNotAffectOrder)
TEST_F(PeerMgrWishlistTest, gotRejectResetsBlock)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1236,7 +1125,7 @@ TEST_F(PeerMgrWishlistTest, gotRejectResetsBlock)
auto wishlist = Wishlist{ mediator };
// we have active requests to the first 250 blocks
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 250 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 250 });
// a peer sent some "Reject" messages, which cancels active requests
auto rejected_bitfield = tr_bitfield{ 300 };
@@ -1244,7 +1133,7 @@ TEST_F(PeerMgrWishlistTest, gotRejectResetsBlock)
{
auto const block = tr_rand_int(250U);
rejected_bitfield.set(block);
got_reject_.emit(nullptr, nullptr, block);
wishlist.on_got_reject(block);
}
return std::pair{ wishlist.next(n_wanted, PeerHasAllPieces), std::move(rejected_bitfield) };
@@ -1275,9 +1164,9 @@ TEST_F(PeerMgrWishlistTest, gotRejectResetsBlock)
TEST_F(PeerMgrWishlistTest, gotRejectResortsPiece)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: two pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1295,12 +1184,12 @@ TEST_F(PeerMgrWishlistTest, gotRejectResortsPiece)
auto wishlist = Wishlist{ mediator };
// we have active requests to the first 50 blocks of each piece
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 50 });
sent_request_.emit(nullptr, nullptr, { .begin = 100, .end = 150 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 50 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 100, .end = 150 });
// a peer sent a "Reject" messages, which cancels active requests
auto const random_piece = tr_rand_int(2U);
got_reject_.emit(nullptr, nullptr, mediator.block_span_[random_piece].begin);
wishlist.on_got_reject(mediator.block_span_[random_piece].begin);
return std::pair{ wishlist.next(n_wanted, PeerHasAllPieces), 1U - random_piece };
};
@@ -1325,9 +1214,9 @@ TEST_F(PeerMgrWishlistTest, gotRejectResortsPiece)
TEST_F(PeerMgrWishlistTest, sentCancelResetsBlocks)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1349,7 +1238,7 @@ TEST_F(PeerMgrWishlistTest, sentCancelResetsBlocks)
auto wishlist = Wishlist{ mediator };
// we have active requests to the first 250 blocks
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 250 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 250 });
// we sent some "Cancel" messages
auto cancelled_bitfield = tr_bitfield{ 300 };
@@ -1357,7 +1246,7 @@ TEST_F(PeerMgrWishlistTest, sentCancelResetsBlocks)
{
auto const block = tr_rand_int(250U);
cancelled_bitfield.set(block);
sent_cancel_.emit(nullptr, nullptr, block);
wishlist.on_sent_cancel(block);
}
return std::pair{ wishlist.next(n_wanted, PeerHasAllPieces), std::move(cancelled_bitfield) };
@@ -1388,9 +1277,9 @@ TEST_F(PeerMgrWishlistTest, sentCancelResetsBlocks)
TEST_F(PeerMgrWishlistTest, doesNotRequestBlockAfterBlockCompleted)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1412,7 +1301,7 @@ TEST_F(PeerMgrWishlistTest, doesNotRequestBlockAfterBlockCompleted)
auto wishlist = Wishlist{ mediator };
// we sent "Request" messages
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 120 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 120 });
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -1439,7 +1328,7 @@ TEST_F(PeerMgrWishlistTest, doesNotRequestBlockAfterBlockCompleted)
TEST_F(PeerMgrWishlistTest, doesNotRequestPieceAfterPieceCompleted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, piece 0 is nearly complete
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1462,14 +1351,14 @@ TEST_F(PeerMgrWishlistTest, doesNotRequestPieceAfterPieceCompleted)
auto wishlist = Wishlist{ mediator };
// we just completed piece 0
sent_request_.emit(nullptr, nullptr, mediator.block_span_[0]);
wishlist.on_sent_request(mediator.block_span_[0]);
for (auto [block, end] = mediator.block_span_[0]; block < end; ++block)
{
mediator.client_has_block_.insert(block);
got_block_.emit(nullptr, block);
wishlist.on_got_block(block);
}
mediator.client_has_piece_.insert(0);
piece_completed_.emit(nullptr, 0);
wishlist.on_piece_completed(0);
// receiving a "piece_completed" signal removes the piece from the
// wishlist's cache, its blocks should not be in the return set.
@@ -1486,9 +1375,9 @@ TEST_F(PeerMgrWishlistTest, doesNotRequestPieceAfterPieceCompleted)
TEST_F(PeerMgrWishlistTest, settingPriorityResortsCandidates)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1512,7 +1401,7 @@ TEST_F(PeerMgrWishlistTest, settingPriorityResortsCandidates)
// a file priority changed, the cache should be rebuilt.
// let's say the file was in piece 1
mediator.piece_priority_[1] = TR_PRI_HIGH;
priority_changed_.emit(nullptr, nullptr, 0U, TR_PRI_HIGH);
wishlist.on_priority_changed();
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -1540,9 +1429,9 @@ TEST_F(PeerMgrWishlistTest, settingPriorityResortsCandidates)
TEST_F(PeerMgrWishlistTest, settingSequentialDownloadResortsCandidates)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: three pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1566,7 +1455,7 @@ TEST_F(PeerMgrWishlistTest, settingSequentialDownloadResortsCandidates)
// the sequential download setting was changed,
// the cache should be rebuilt
mediator.is_sequential_download_ = true;
sequential_download_changed_.emit(nullptr, true);
wishlist.on_sequential_download_changed();
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -1609,9 +1498,9 @@ TEST_F(PeerMgrWishlistTest, settingSequentialDownloadResortsCandidates)
TEST_F(PeerMgrWishlistTest, sequentialDownloadFromPieceResortsCandidates)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: four pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1636,9 +1525,9 @@ TEST_F(PeerMgrWishlistTest, sequentialDownloadFromPieceResortsCandidates)
// we enabled sequential download, from piece 2
mediator.is_sequential_download_ = true;
sequential_download_changed_.emit(nullptr, true);
wishlist.on_sequential_download_changed();
mediator.sequential_download_from_piece_ = 2;
sequential_download_from_piece_changed_.emit(nullptr, 2);
wishlist.on_sequential_download_from_piece_changed();
// the sequential download setting was changed,
// the candidate list should be resorted
@@ -1671,9 +1560,9 @@ TEST_F(PeerMgrWishlistTest, sequentialDownloadFromPieceResortsCandidates)
TEST_F(PeerMgrWishlistTest, setFileWantedUpdatesCandidateListAdd)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: four pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1697,7 +1586,7 @@ TEST_F(PeerMgrWishlistTest, setFileWantedUpdatesCandidateListAdd)
// now we want the file that consists of piece 2 and piece 3 also
mediator.client_wants_piece_.insert(2);
mediator.client_wants_piece_.insert(3);
files_wanted_changed_.emit(nullptr, nullptr, 0, true);
wishlist.on_files_wanted_changed();
// a candidate should be inserted into the wishlist for
// piece 2 and piece 3
@@ -1727,9 +1616,9 @@ TEST_F(PeerMgrWishlistTest, setFileWantedUpdatesCandidateListAdd)
TEST_F(PeerMgrWishlistTest, setFileWantedUpdatesCandidateListAddHad)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: four pieces
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1757,7 +1646,7 @@ TEST_F(PeerMgrWishlistTest, setFileWantedUpdatesCandidateListAddHad)
// now we want piece 2 and piece 3
mediator.client_wants_piece_.insert(2);
mediator.client_wants_piece_.insert(3);
files_wanted_changed_.emit(nullptr, nullptr, 0, true);
wishlist.on_files_wanted_changed();
// the candidate list should remain unchanged
return wishlist.next(n_wanted, PeerHasAllPieces);
@@ -1786,9 +1675,9 @@ TEST_F(PeerMgrWishlistTest, setFileWantedUpdatesCandidateListAddHad)
TEST_F(PeerMgrWishlistTest, setFileWantedUpdatesCandidateListRemove)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: four pieces, all missing
mediator.block_span_[0] = { .begin = 0, .end = 100 };
@@ -1814,7 +1703,7 @@ TEST_F(PeerMgrWishlistTest, setFileWantedUpdatesCandidateListRemove)
// we no longer want the file that consists of piece 2 and piece 3
mediator.client_wants_piece_.erase(2);
mediator.client_wants_piece_.erase(3);
files_wanted_changed_.emit(nullptr, nullptr, 0, true);
wishlist.on_files_wanted_changed();
// the candidate objects for piece 2 and piece 3 should be removed
return wishlist.next(n_wanted, PeerHasAllPieces);
@@ -1843,9 +1732,9 @@ TEST_F(PeerMgrWishlistTest, setFileWantedUpdatesCandidateListRemove)
TEST_F(PeerMgrWishlistTest, unalignedTorrent)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each, all missing
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -1898,9 +1787,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrent)
TEST_F(PeerMgrWishlistTest, unalignedTorrentPartiallyCompletedPiece)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -1969,9 +1858,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentPartiallyCompletedPiece)
TEST_F(PeerMgrWishlistTest, unalignedTorrentPartiallyCompleted)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -2025,9 +1914,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentPartiallyCompleted)
TEST_F(PeerMgrWishlistTest, unalignedTorrentGotBadPiece)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -2054,11 +1943,11 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentGotBadPiece)
// requested all blocks and "download" piece 1,
// as well as parts of piece 0 and piece 2 that
// is next to piece 1
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 134 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 134 });
for (auto block = mediator.block_span_[0].end - 10; block < mediator.block_span_[1].end + 10; ++block)
{
mediator.client_has_block_.insert(block);
got_block_.emit(nullptr, block);
wishlist.on_got_block(block);
}
// piece 1 turned out to be corrupt, needs to be re-downloaded
@@ -2066,7 +1955,7 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentGotBadPiece)
{
mediator.client_has_block_.erase(block);
}
got_bad_piece_.emit(nullptr, 1);
wishlist.on_got_bad_piece(1);
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -2093,9 +1982,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentGotBadPiece)
TEST_F(PeerMgrWishlistTest, unalignedTorrentGotBadPieceSurroundingCompleted)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -2120,23 +2009,23 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentGotBadPieceSurroundingCompleted)
auto wishlist = Wishlist{ mediator };
// pieces 0, 2 completed normally, piece 3 has pending requests
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 134 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 134 });
for (tr_block_index_t block = 0; block < 120; ++block)
{
mediator.client_has_block_.insert(block);
got_block_.emit(nullptr, block);
wishlist.on_got_block(block);
}
mediator.client_has_piece_.insert(0);
piece_completed_.emit(nullptr, 0);
wishlist.on_piece_completed(0);
mediator.client_has_piece_.insert(2);
piece_completed_.emit(nullptr, 2);
wishlist.on_piece_completed(2);
// piece 1 turned out to be corrupt, needs to be re-downloaded
for (auto [block, end] = mediator.block_span_[1]; block < end; ++block)
{
mediator.client_has_block_.erase(block);
}
got_bad_piece_.emit(nullptr, 1);
wishlist.on_got_bad_piece(1);
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -2163,9 +2052,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentGotBadPieceSurroundingCompleted)
TEST_F(PeerMgrWishlistTest, unalignedTorrentGot2ConsectutiveBadPieces)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -2190,28 +2079,28 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentGot2ConsectutiveBadPieces)
auto wishlist = Wishlist{ mediator };
// pieces 0, 3 completed normally
sent_request_.emit(nullptr, nullptr, { .begin = 0, .end = 134 });
wishlist.on_sent_request(tr_block_span_t{ .begin = 0, .end = 134 });
for (tr_block_index_t block = 0; block < 134; ++block)
{
mediator.client_has_block_.insert(block);
got_block_.emit(nullptr, block);
wishlist.on_got_block(block);
}
mediator.client_has_piece_.insert(0);
piece_completed_.emit(nullptr, 0);
wishlist.on_piece_completed(0);
mediator.client_has_piece_.insert(3);
piece_completed_.emit(nullptr, 3);
wishlist.on_piece_completed(3);
// pieces 1, 2 turned out to be corrupt, need to be re-downloaded
for (auto [block, end] = mediator.block_span_[1]; block < end; ++block)
{
mediator.client_has_block_.erase(block);
}
got_bad_piece_.emit(nullptr, 1);
wishlist.on_got_bad_piece(1);
for (auto [block, end] = mediator.block_span_[2]; block < end; ++block)
{
mediator.client_has_block_.erase(block);
}
got_bad_piece_.emit(nullptr, 2);
wishlist.on_got_bad_piece(2);
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -2243,9 +2132,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentGot2ConsectutiveBadPieces)
TEST_F(PeerMgrWishlistTest, unalignedTorrentPartiallyWanted)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each, all missing
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -2296,9 +2185,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentPartiallyWanted)
TEST_F(PeerMgrWishlistTest, unalignedTorrentDeselectedPiece)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each, all missing
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -2325,7 +2214,7 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentDeselectedPiece)
// we don't want piece 1 anymore
tr_file_index_t constexpr Deselected = 1;
mediator.client_wants_piece_.erase(Deselected);
files_wanted_changed_.emit(nullptr, &Deselected, 1, false);
wishlist.on_files_wanted_changed();
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -2356,9 +2245,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentDeselectedPiece)
TEST_F(PeerMgrWishlistTest, unalignedTorrentDeselected2ConsecutivePieces)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each, all missing
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -2388,7 +2277,7 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentDeselected2ConsecutivePieces)
{
mediator.client_wants_piece_.erase(idx);
}
files_wanted_changed_.emit(nullptr, std::data(Deselected), std::size(Deselected), false);
wishlist.on_files_wanted_changed();
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -2419,9 +2308,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentDeselected2ConsecutivePieces)
TEST_F(PeerMgrWishlistTest, unalignedTorrentSelectedPiece)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each, all missing
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -2447,7 +2336,7 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentSelectedPiece)
// we want piece 1 now
tr_file_index_t constexpr Selected = 1;
mediator.client_wants_piece_.insert(Selected);
files_wanted_changed_.emit(nullptr, &Selected, 1, true);
wishlist.on_files_wanted_changed();
return wishlist.next(n_wanted, PeerHasAllPieces);
};
@@ -2476,9 +2365,9 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentSelectedPiece)
TEST_F(PeerMgrWishlistTest, unalignedTorrentSelected2ConsecutivePieces)
{
auto const get_spans = [this](size_t n_wanted)
auto const get_spans = [](size_t n_wanted)
{
auto mediator = MockMediator{ *this };
auto mediator = MockMediator{};
// setup: 4 pieces, (100 / 3 * 16) KiB each, all missing
// N.B. only the boundary of piece 2 and 3 is aligned
@@ -2506,7 +2395,7 @@ TEST_F(PeerMgrWishlistTest, unalignedTorrentSelected2ConsecutivePieces)
{
mediator.client_wants_piece_.insert(idx);
}
files_wanted_changed_.emit(nullptr, std::data(Selected), std::size(Selected), true);
wishlist.on_files_wanted_changed();
return wishlist.next(n_wanted, PeerHasAllPieces);
};

1
third-party/sigslot vendored Submodule

Submodule third-party/sigslot added at b588b791b9