diff --git a/.gitmodules b/.gitmodules index d4c7f2c6a..626f6ad82 100644 --- a/.gitmodules +++ b/.gitmodules @@ -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 diff --git a/Transmission.xcodeproj/project.pbxproj b/Transmission.xcodeproj/project.pbxproj index 92da1baa0..12e12da15 100644 --- a/Transmission.xcodeproj/project.pbxproj +++ b/Transmission.xcodeproj/project.pbxproj @@ -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", ); }; diff --git a/libtransmission/CMakeLists.txt b/libtransmission/CMakeLists.txt index 20c17fa83..6f77e4026 100644 --- a/libtransmission/CMakeLists.txt +++ b/libtransmission/CMakeLists.txt @@ -12,6 +12,8 @@ add_compile_options( # equivalent of XCODE_ATTRIBUTE_CLANG_ENABLE_OBJC_ARC YES for this directory $<$,$,$>,$,$>>:-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) diff --git a/libtransmission/blocklist.cc b/libtransmission/blocklist.cc index 319f61ee9..6bf6860ca 100644 --- a/libtransmission/blocklist.cc +++ b/libtransmission/blocklist.cc @@ -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; } diff --git a/libtransmission/blocklist.h b/libtransmission/blocklist.h index 92a61fd1e..3263f28ac 100644 --- a/libtransmission/blocklist.h +++ b/libtransmission/blocklist.h @@ -18,8 +18,9 @@ #include // for std::pair #include +#include + #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 - [[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 load_folder(std::string_view folder, bool is_enabled); }; diff --git a/libtransmission/observable.h b/libtransmission/observable.h deleted file mode 100644 index b0d85d9fa..000000000 --- a/libtransmission/observable.h +++ /dev/null @@ -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 // for size_t -#include -#include // for std::move - -#include - -#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; - - ObserverTag() = default; - - ObserverTag(ObserverTag&& that) noexcept - { - *this = std::forward(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 -class SimpleObservable // NOLINT(cppcoreguidelines-special-member-functions) -{ - using Key = size_t; - -public: - using Observer = std::function; - - ~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 observers_; -}; - -} // namespace tr diff --git a/libtransmission/peer-mgr-wishlist.cc b/libtransmission/peer-mgr-wishlist.cc index 8173b3f92..6e08a565d 100644 --- a/libtransmission/peer-mgr-wishlist.cc +++ b/libtransmission/peer-mgr-wishlist.cc @@ -4,6 +4,7 @@ // License text can be found in the licenses/ folder. #include // std::adjacent_find, std::sort +#include #include #include #include @@ -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 next( size_t n_wanted_blocks, std::function const& peer_has_piece); @@ -523,49 +600,11 @@ private: CandidateVec candidates_; - std::array 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 Wishlist::next( size_t const n_wanted_blocks, std::function const& peer_has_piece) diff --git a/libtransmission/peer-mgr-wishlist.h b/libtransmission/peer-mgr-wishlist.h index cf944837f..8f1eeb80b 100644 --- a/libtransmission/peer-mgr-wishlist.h +++ b/libtransmission/peer-mgr-wishlist.h @@ -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::Observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_peer_disconnect( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_got_bad_piece( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_got_bitfield( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_got_block( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_got_choke( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_got_have( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_got_have_all(tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_got_reject( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_piece_completed( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_priority_changed( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_sent_cancel( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_sent_request( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_sequential_download_changed( - tr::SimpleObservable::Observer observer) = 0; - [[nodiscard]] virtual tr::ObserverTag observe_sequential_download_from_piece_changed( - tr::SimpleObservable::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 next( size_t n_wanted_blocks, diff --git a/libtransmission/peer-mgr.cc b/libtransmission/peer-mgr.cc index c26410224..87b08d331 100644 --- a/libtransmission/peer-mgr.cc +++ b/libtransmission/peer-mgr.cc @@ -23,6 +23,8 @@ #include #include +#include + #include #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>; using Pool = small::map>; - 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 const& peer_has_piece) + { + return wishlist_.next(n_wanted_blocks, peer_has_piece); + } - [[nodiscard]] tr::ObserverTag observe_files_wanted_changed( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_peer_disconnect( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_got_bad_piece( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_got_bitfield( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_got_block( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_got_choke( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_got_have( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_got_have_all(tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_got_reject( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_piece_completed( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_priority_changed( - tr::SimpleObservable::Observer observer) - override; - [[nodiscard]] tr::ObserverTag observe_sent_cancel( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_sent_request( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_sequential_download_changed( - tr::SimpleObservable::Observer observer) override; - [[nodiscard]] tr::ObserverTag observe_sequential_download_from_piece_changed( - tr::SimpleObservable::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 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 peer_disconnect; - tr::SimpleObservable got_bitfield; - tr::SimpleObservable got_block; - tr::SimpleObservable got_choke; - tr::SimpleObservable got_have; - tr::SimpleObservable got_have_all; - tr::SimpleObservable got_reject; - tr::SimpleObservable sent_cancel; - tr::SimpleObservable sent_request; + sigslot::signal peer_disconnect; + sigslot::signal got_bitfield; + sigslot::signal got_block; + sigslot::signal got_choke; + sigslot::signal got_have; + sigslot::signal got_have_all; + sigslot::signal got_reject; + sigslot::signal sent_cancel; + sigslot::signal 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; + std::unique_ptr 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 const tags_; + std::array const tags_; mutable std::optional 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::Observer observer) -{ - return tor_.files_wanted_changed_.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_peer_disconnect( - tr::SimpleObservable::Observer observer) -{ - return swarm_.peer_disconnect.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_got_bad_piece( - tr::SimpleObservable::Observer observer) -{ - return tor_.got_bad_piece_.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_got_bitfield( - tr::SimpleObservable::Observer observer) -{ - return swarm_.got_bitfield.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_got_block( - tr::SimpleObservable::Observer observer) -{ - return swarm_.got_block.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_got_choke( - tr::SimpleObservable::Observer observer) -{ - return swarm_.got_choke.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_got_have( - tr::SimpleObservable::Observer observer) -{ - return swarm_.got_have.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_got_have_all(tr::SimpleObservable::Observer observer) -{ - return swarm_.got_have_all.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_got_reject( - tr::SimpleObservable::Observer observer) -{ - return swarm_.got_reject.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_piece_completed( - tr::SimpleObservable::Observer observer) -{ - return tor_.piece_completed_.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_priority_changed( - tr::SimpleObservable::Observer observer) -{ - return tor_.priority_changed_.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_sent_cancel( - tr::SimpleObservable::Observer observer) -{ - return swarm_.sent_cancel.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_sent_request( - tr::SimpleObservable::Observer observer) -{ - return swarm_.sent_request.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_sequential_download_changed( - tr::SimpleObservable::Observer observer) -{ - return tor_.sequential_download_changed_.observe(std::move(observer)); -} - -tr::ObserverTag tr_swarm::WishlistMediator::observe_sequential_download_from_piece_changed( - tr::SimpleObservable::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, 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 const peer_info_timer_; std::unique_ptr 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_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_mediator); + wishlist_controller = std::make_unique(*this); } void tr_swarm::on_torrent_stopped() diff --git a/libtransmission/torrent.cc b/libtransmission/torrent.cc index 50ef1f59d..08f8c3ddb 100644 --- a/libtransmission/torrent.cc +++ b/libtransmission/torrent.cc @@ -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); } diff --git a/libtransmission/torrent.h b/libtransmission/torrent.h index fc63ff91d..20e266d8f 100644 --- a/libtransmission/torrent.h +++ b/libtransmission/torrent.h @@ -20,6 +20,8 @@ #include #include +#include + #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 done_; - tr::SimpleObservable got_bad_piece_; - tr::SimpleObservable piece_completed_; - tr::SimpleObservable doomed_; - tr::SimpleObservable got_metainfo_; - tr::SimpleObservable started_; - tr::SimpleObservable stopped_; - tr::SimpleObservable swarm_is_all_upload_only_; - tr::SimpleObservable files_wanted_changed_; - tr::SimpleObservable priority_changed_; - tr::SimpleObservable sequential_download_changed_; - tr::SimpleObservable sequential_download_from_piece_changed_; + sigslot::signal done_; + sigslot::signal got_bad_piece_; + sigslot::signal piece_completed_; + sigslot::signal doomed_; + sigslot::signal got_metainfo_; + sigslot::signal started_; + sigslot::signal stopped_; + sigslot::signal swarm_is_all_upload_only_; + sigslot::signal files_wanted_changed_; + sigslot::signal priority_changed_; + sigslot::signal sequential_download_changed_; + sigslot::signal 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) { diff --git a/tests/libtransmission/peer-mgr-wishlist-test.cc b/tests/libtransmission/peer-mgr-wishlist-test.cc index 56c745d33..3081e7c3f 100644 --- a/tests/libtransmission/peer-mgr-wishlist-test.cc +++ b/tests/libtransmission/peer-mgr-wishlist-test.cc @@ -3,9 +3,9 @@ // or any future license endorsed by Mnemosyne LLC. // License text can be found in the licenses/ folder. +#include #include // size_t #include -#include #include #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::Observer observer) override - { - return parent_.files_wanted_changed_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_peer_disconnect( - tr::SimpleObservable::Observer observer) override - { - return parent_.peer_disconnect_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_got_bad_piece( - tr::SimpleObservable::Observer observer) override - { - return parent_.got_bad_piece_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_got_bitfield( - tr::SimpleObservable::Observer observer) override - { - return parent_.got_bitfield_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_got_block( - tr::SimpleObservable::Observer observer) override - { - return parent_.got_block_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_got_choke( - tr::SimpleObservable::Observer observer) override - { - return parent_.got_choke_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_got_have( - tr::SimpleObservable::Observer observer) override - { - return parent_.got_have_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_got_have_all(tr::SimpleObservable::Observer observer) override - { - return parent_.got_have_all_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_got_reject( - tr::SimpleObservable::Observer observer) override - { - return parent_.got_reject_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_piece_completed( - tr::SimpleObservable::Observer observer) override - { - return parent_.piece_completed_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_priority_changed( - tr::SimpleObservable::Observer observer) - override - { - return parent_.priority_changed_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_sent_cancel( - tr::SimpleObservable::Observer observer) override - { - return parent_.sent_cancel_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_sent_request( - tr::SimpleObservable::Observer observer) override - { - return parent_.sent_request_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_sequential_download_changed( - tr::SimpleObservable::Observer observer) override - { - return parent_.sequential_download_changed_.observe(std::move(observer)); - } - - [[nodiscard]] tr::ObserverTag observe_sequential_download_from_piece_changed( - tr::SimpleObservable::Observer observer) override - { - return parent_.sequential_download_from_piece_changed_.observe(std::move(observer)); - } }; - tr::SimpleObservable files_wanted_changed_; - tr::SimpleObservable peer_disconnect_; - tr::SimpleObservable got_bad_piece_; - tr::SimpleObservable got_bitfield_; - tr::SimpleObservable got_block_; - tr::SimpleObservable got_choke_; - tr::SimpleObservable got_have_; - tr::SimpleObservable got_have_all_; - tr::SimpleObservable got_reject_; - tr::SimpleObservable sent_cancel_; - tr::SimpleObservable sent_request_; - tr::SimpleObservable piece_completed_; - tr::SimpleObservable priority_changed_; - tr::SimpleObservable sequential_download_changed_; - tr::SimpleObservable 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); }; diff --git a/third-party/sigslot b/third-party/sigslot new file mode 160000 index 000000000..b588b791b --- /dev/null +++ b/third-party/sigslot @@ -0,0 +1 @@ +Subproject commit b588b791b9cf7eb17ff0a74d8aebd4a61166c2e1