From 4bd3a7826dbfdc1350015345351bb82b5c01917e Mon Sep 17 00:00:00 2001 From: kamilsa Date: Wed, 11 Jun 2025 14:49:02 +0500 Subject: [PATCH 1/2] feat: add scheduler support for timeout management in Multiselect --- include/libp2p/protocol_muxer/multiselect.hpp | 9 ++++++ .../multiselect/multiselect_instance.hpp | 17 ++++++++++- src/protocol_muxer/multiselect.cpp | 6 +++- .../multiselect/multiselect_instance.cpp | 30 +++++++++++++++++-- 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/include/libp2p/protocol_muxer/multiselect.hpp b/include/libp2p/protocol_muxer/multiselect.hpp index 1ccc0b234..7ad9a3ea3 100644 --- a/include/libp2p/protocol_muxer/multiselect.hpp +++ b/include/libp2p/protocol_muxer/multiselect.hpp @@ -11,6 +11,10 @@ #include "protocol_muxer.hpp" +namespace libp2p::basic { + class Scheduler; +} + namespace libp2p::protocol_muxer::multiselect { class MultiselectInstance; @@ -20,6 +24,8 @@ namespace libp2p::protocol_muxer::multiselect { public: using Instance = std::shared_ptr; + explicit Multiselect(std::shared_ptr scheduler); + ~Multiselect() override = default; /// Implements ProtocolMuxer API @@ -45,6 +51,9 @@ namespace libp2p::protocol_muxer::multiselect { /// Returns instance either from cache or creates a new one Instance getInstance(); + /// Scheduler for timeout management + std::shared_ptr scheduler_; + /// Active instances, keep them here to hold shared ptrs alive std::unordered_set active_instances_; diff --git a/include/libp2p/protocol_muxer/multiselect/multiselect_instance.hpp b/include/libp2p/protocol_muxer/multiselect/multiselect_instance.hpp index 46eab6cbe..4133332d8 100644 --- a/include/libp2p/protocol_muxer/multiselect/multiselect_instance.hpp +++ b/include/libp2p/protocol_muxer/multiselect/multiselect_instance.hpp @@ -6,6 +6,7 @@ #pragma once +#include #include #include "parser.hpp" @@ -13,6 +14,10 @@ namespace soralog { class Logger; } +namespace libp2p::basic { + class Scheduler; +} + namespace libp2p::protocol_muxer::multiselect { class Multiselect; @@ -21,7 +26,8 @@ namespace libp2p::protocol_muxer::multiselect { class MultiselectInstance : public std::enable_shared_from_this { public: - explicit MultiselectInstance(Multiselect &owner); + explicit MultiselectInstance(Multiselect &owner, + std::shared_ptr scheduler); /// Implements ProtocolMuxer API void selectOneOf(std::span protocols, @@ -74,6 +80,9 @@ namespace libp2p::protocol_muxer::multiselect { /// Handles "na" reply, client-specific MaybeResult handleNA(); + /// Handles timeout when negotiation takes too long + void onTimeout(); + /// Owner of this object, needed for reuse of instances Multiselect &owner_; @@ -125,6 +134,12 @@ namespace libp2p::protocol_muxer::multiselect { /// Cache: serialized NA response boost::optional na_response_; + + /// Scheduler for timeout handling + std::shared_ptr scheduler_; + + /// Timeout handle for negotiation timeout + basic::Scheduler::Handle timeout_handle_; }; } // namespace libp2p::protocol_muxer::multiselect diff --git a/src/protocol_muxer/multiselect.cpp b/src/protocol_muxer/multiselect.cpp index 86778d53e..59a79aa09 100644 --- a/src/protocol_muxer/multiselect.cpp +++ b/src/protocol_muxer/multiselect.cpp @@ -4,6 +4,7 @@ * SPDX-License-Identifier: Apache-2.0 */ +#include #include #include #include @@ -21,6 +22,9 @@ namespace libp2p::protocol_muxer::multiselect { constexpr size_t kMaxCacheSize = 8; } // namespace + Multiselect::Multiselect(std::shared_ptr scheduler) + : scheduler_(std::move(scheduler)) {} + void Multiselect::selectOneOf(std::span protocols, std::shared_ptr connection, bool is_initiator, @@ -62,7 +66,7 @@ namespace libp2p::protocol_muxer::multiselect { Multiselect::Instance Multiselect::getInstance() { Instance instance; if (cache_.empty()) { - instance = std::make_shared(*this); + instance = std::make_shared(*this, scheduler_); } else { SL_TRACE(log(), "cache: {}->{}, active {}->{}", diff --git a/src/protocol_muxer/multiselect/multiselect_instance.cpp b/src/protocol_muxer/multiselect/multiselect_instance.cpp index abb657bf0..f48a0d0e9 100644 --- a/src/protocol_muxer/multiselect/multiselect_instance.cpp +++ b/src/protocol_muxer/multiselect/multiselect_instance.cpp @@ -7,7 +7,9 @@ #include #include +#include +#include #include #include #include @@ -20,10 +22,14 @@ namespace libp2p::protocol_muxer::multiselect { static log::Logger logger = log::createLogger("Multiselect"); return logger; } + + /// Timeout for protocol negotiation (5 seconds) + constexpr std::chrono::milliseconds kNegotiationTimeout{5000}; } // namespace - MultiselectInstance::MultiselectInstance(Multiselect &owner) - : owner_(owner) {} + MultiselectInstance::MultiselectInstance( + Multiselect &owner, std::shared_ptr scheduler) + : owner_(owner), scheduler_(std::move(scheduler)) {} void MultiselectInstance::selectOneOf( std::span protocols, @@ -62,6 +68,15 @@ namespace libp2p::protocol_muxer::multiselect { write_queue_.clear(); is_writing_ = false; + // Schedule timeout for negotiation + timeout_handle_ = scheduler_->scheduleWithHandle( + [wptr = std::weak_ptr(shared_from_this())]() { + if (auto self = wptr.lock()) { + self->onTimeout(); + } + }, + kNegotiationTimeout); + if (is_initiator_) { std::ignore = sendProposal(); } else if (negotiate_multiselect) { @@ -166,6 +181,10 @@ namespace libp2p::protocol_muxer::multiselect { void MultiselectInstance::close(outcome::result result) { closed_ = true; + + // Cancel timeout if it's still active + timeout_handle_.reset(); + ++current_round_; write_queue_.clear(); Multiselect::ProtocolHandlerFunc callback; @@ -338,4 +357,11 @@ namespace libp2p::protocol_muxer::multiselect { return MaybeResult(ProtocolMuxer::Error::PROTOCOL_VIOLATION); } + void MultiselectInstance::onTimeout() { + SL_DEBUG(log(), + "Protocol negotiation timeout after {}ms", + kNegotiationTimeout.count()); + close(ProtocolMuxer::Error::NEGOTIATION_FAILED); + } + } // namespace libp2p::protocol_muxer::multiselect From 5041b21be92c8ba108fbb70643c25050ed73a012 Mon Sep 17 00:00:00 2001 From: kamilsa Date: Wed, 11 Jun 2025 16:42:30 +0500 Subject: [PATCH 2/2] fix: pass scheduler to Multiselect for improved timeout management --- test/acceptance/p2p/host/peer/test_peer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/acceptance/p2p/host/peer/test_peer.cpp b/test/acceptance/p2p/host/peer/test_peer.cpp index 6c694f357..4041c841b 100644 --- a/test/acceptance/p2p/host/peer/test_peer.cpp +++ b/test/acceptance/p2p/host/peer/test_peer.cpp @@ -136,7 +136,7 @@ Peer::sptr Peer::makeHost(const crypto::KeyPair &keyPair) { std::make_shared(keyPair, key_marshaller); auto multiselect = - std::make_shared(); + std::make_shared(scheduler_); auto router = std::make_shared();