From 0e712b3812d56a65c03c44016a3181fe979cf69f Mon Sep 17 00:00:00 2001 From: Matthew Zipkin Date: Tue, 10 Mar 2026 14:59:35 -0400 Subject: [PATCH] Make DynSock accepted sockets queue optional, with precise lifetime When DynSock is used to represent a connected socket (e.g. a client) the data I/O pipes are needed but not the m_accepted_sockets Queue, because connected sockets do not create more connected sockets. When DynSock is used to represent a listening socket, the Queue is necessary to create connected sockets upon mocked connection, but the Queue does not need to be a std::shared_ptr as long as it is guaranteed to live as long as the DynSock. Co-Authored by: Hodlinator <172445034+hodlinator@users.noreply.github.com> --- src/test/util/net.cpp | 10 ++++++++-- src/test/util/net.h | 13 +++++++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/test/util/net.cpp b/src/test/util/net.cpp index 73e5a58d5d9..15903ac9fd0 100644 --- a/src/test/util/net.cpp +++ b/src/test/util/net.cpp @@ -346,11 +346,16 @@ void DynSock::Pipe::WaitForDataOrEof(UniqueLock& lock) }); } -DynSock::DynSock(std::shared_ptr pipes, std::shared_ptr accept_sockets) +DynSock::DynSock(std::shared_ptr pipes, Queue* accept_sockets) : m_pipes{pipes}, m_accept_sockets{accept_sockets} { } +DynSock::DynSock(std::shared_ptr pipes) + : m_pipes{pipes}, m_accept_sockets{} +{ +} + DynSock::~DynSock() { m_pipes->send.Eof(); @@ -369,6 +374,7 @@ ssize_t DynSock::Send(const void* buf, size_t len, int) const std::unique_ptr DynSock::Accept(sockaddr* addr, socklen_t* addr_len) const { + assert(m_accept_sockets && "Accept() called on non-listening DynSock"); ZeroSock::Accept(addr, addr_len); return m_accept_sockets->Pop().value_or(nullptr); } @@ -403,7 +409,7 @@ bool DynSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_ if ((events.requested & Sock::RECV) != 0) { auto dyn_sock = reinterpret_cast(sock.get()); uint8_t b; - if (dyn_sock->m_pipes->recv.GetBytes(&b, 1, MSG_PEEK) == 1 || !dyn_sock->m_accept_sockets->Empty()) { + if (dyn_sock->m_pipes->recv.GetBytes(&b, 1, MSG_PEEK) == 1 || (dyn_sock->m_accept_sockets && !dyn_sock->m_accept_sockets->Empty())) { events.occurred |= Sock::RECV; at_least_one_event_occurred = true; } diff --git a/src/test/util/net.h b/src/test/util/net.h index 247eba8ea0f..8954e631d3d 100644 --- a/src/test/util/net.h +++ b/src/test/util/net.h @@ -5,6 +5,7 @@ #ifndef BITCOIN_TEST_UTIL_NET_H #define BITCOIN_TEST_UTIL_NET_H +#include #include #include #include @@ -336,7 +337,15 @@ public: * @param[in] pipes Send/recv pipes used by the Send() and Recv() methods. * @param[in] accept_sockets Sockets to return by the Accept() method. */ - explicit DynSock(std::shared_ptr pipes, std::shared_ptr accept_sockets); + explicit DynSock(std::shared_ptr pipes, Queue* accept_sockets LIFETIMEBOUND); + + /** + * Create a new mocked sock that represents a connected socket. It has pipes + * for data transport but there is no queue because connected sockets do + * not introduce new connected sockets. + * @param[in] pipes Send/recv pipes used by the Send() and Recv() methods. + */ + explicit DynSock(std::shared_ptr pipes); ~DynSock(); @@ -356,7 +365,7 @@ private: DynSock& operator=(Sock&&) override; std::shared_ptr m_pipes; - std::shared_ptr m_accept_sockets; + Queue* const m_accept_sockets; }; template