From 13e14a7a0b24b0a797ad54d91b2476976b368bff Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Sat, 18 May 2024 20:23:17 +0000 Subject: [PATCH 1/8] refactor: make `Sock` aware of event mode and applicable file descriptor --- src/i2p.cpp | 2 +- src/masternode/node.cpp | 2 +- src/net.cpp | 8 ++++---- src/net.h | 7 +++++++ src/netbase.cpp | 6 +++--- src/netbase.h | 7 +++++-- src/test/fuzz/fuzz.cpp | 2 +- src/test/fuzz/i2p.cpp | 2 +- src/test/i2p_tests.cpp | 2 +- src/util/sock.cpp | 30 ++++++++++++++++++++++++++++-- src/util/sock.h | 7 ++++++- 11 files changed, 58 insertions(+), 17 deletions(-) diff --git a/src/i2p.cpp b/src/i2p.cpp index 1175366eb5d38..7036642039955 100644 --- a/src/i2p.cpp +++ b/src/i2p.cpp @@ -295,7 +295,7 @@ Session::Reply Session::SendRequestAndGetReply(const Sock& sock, std::unique_ptr Session::Hello() const { - auto sock = CreateSock(m_control_host); + auto sock = CreateSock(m_control_host, SocketEventsMode::Select, /* fd_mode = */ std::nullopt); if (!sock) { throw std::runtime_error("Cannot create socket"); diff --git a/src/masternode/node.cpp b/src/masternode/node.cpp index 61d42c4d31499..d200b8559d732 100644 --- a/src/masternode/node.cpp +++ b/src/masternode/node.cpp @@ -155,7 +155,7 @@ void CActiveMasternodeManager::InitInternal(const CBlockIndex* pindex) // Check socket connectivity LogPrintf("CActiveMasternodeManager::Init -- Checking inbound connection to '%s'\n", m_info.service.ToStringAddrPort()); - std::unique_ptr sock = CreateSock(m_info.service); + std::unique_ptr sock = CreateSock(m_info.service, SocketEventsMode::Select, /* fd_mode = */ std::nullopt); if (!sock) { m_state = MASTERNODE_ERROR; m_error = "Could not create socket to connect to " + m_info.service.ToStringAddrPort(); diff --git a/src/net.cpp b/src/net.cpp index a49132c8dfc77..3c157898a050f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -577,7 +577,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo addr_bind = CAddress{conn.me, NODE_NONE}; } } else if (use_proxy) { - sock = CreateSock(proxy.proxy); + sock = CreateSock(proxy.proxy, socketEventsMode, GetModeFileDescriptor()); if (!sock) { return nullptr; } @@ -585,7 +585,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo *sock, nConnectTimeout, proxyConnectionFailed); } else { // no proxy needed (none set for target network) - sock = CreateSock(addrConnect); + sock = CreateSock(addrConnect, socketEventsMode, GetModeFileDescriptor()); if (!sock) { return nullptr; } @@ -597,7 +597,7 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo addrman.Attempt(addrConnect, fCountFailure); } } else if (pszDest && GetNameProxy(proxy)) { - sock = CreateSock(proxy.proxy); + sock = CreateSock(proxy.proxy, socketEventsMode, GetModeFileDescriptor()); if (!sock) { return nullptr; } @@ -3837,7 +3837,7 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, return false; } - std::unique_ptr sock = CreateSock(addrBind); + std::unique_ptr sock = CreateSock(addrBind, socketEventsMode, GetModeFileDescriptor()); if (!sock) { strError = strprintf(Untranslated("Error: Couldn't open socket for incoming connections (socket returned error %s)"), NetworkErrorString(WSAGetLastError())); LogPrintf("%s\n", strError.original); diff --git a/src/net.h b/src/net.h index ab17ca77587d9..03a1487045bb0 100644 --- a/src/net.h +++ b/src/net.h @@ -1843,6 +1843,13 @@ friend class CNode; std::unique_ptr m_edge_trig_events{nullptr}; std::unique_ptr m_wakeup_pipe{nullptr}; + std::optional GetModeFileDescriptor() { + if (m_edge_trig_events) { + return m_edge_trig_events->GetFileDescriptor(); + } + return std::nullopt; + } + template void ToggleWakeupPipe(Callable&& func) { diff --git a/src/netbase.cpp b/src/netbase.cpp index f784017bf0a7c..6826dddc37c0d 100644 --- a/src/netbase.cpp +++ b/src/netbase.cpp @@ -456,7 +456,7 @@ bool Socks5(const std::string& strDest, uint16_t port, const ProxyCredentials* a return true; } -std::unique_ptr CreateSockTCP(const CService& address_family) +std::unique_ptr CreateSockTCP(const CService& address_family, SocketEventsMode event_mode, std::optional fd_mode) { // Create a sockaddr from the specified service. struct sockaddr_storage sockaddr; @@ -472,7 +472,7 @@ std::unique_ptr CreateSockTCP(const CService& address_family) return nullptr; } - auto sock = std::make_unique(hSocket); + auto sock = std::make_unique(hSocket, event_mode, fd_mode); // Ensure that waiting for I/O on this socket won't result in undefined // behavior. @@ -505,7 +505,7 @@ std::unique_ptr CreateSockTCP(const CService& address_family) return sock; } -std::function(const CService&)> CreateSock = CreateSockTCP; +std::function(const CService&, SocketEventsMode, std::optional)> CreateSock = CreateSockTCP; template static void LogConnectFailure(bool manual_connection, const char* fmt, const Args&... args) { diff --git a/src/netbase.h b/src/netbase.h index 9478555d59484..9e7897262058e 100644 --- a/src/netbase.h +++ b/src/netbase.h @@ -184,14 +184,17 @@ bool LookupSubNet(const std::string& subnet_str, CSubNet& subnet_out); /** * Create a TCP socket in the given address family. * @param[in] address_family The socket is created in the same address family as this address. + * @param[in] event_mode The socket enables additional behaviour based on the set event mode. + * @param[in] fd_mode Optional file descriptor for managing instances for applicable event modes. + * They are optional for all modes except epoll and kqueue. * @return pointer to the created Sock object or unique_ptr that owns nothing in case of failure */ -std::unique_ptr CreateSockTCP(const CService& address_family); +std::unique_ptr CreateSockTCP(const CService& address_family, SocketEventsMode event_mode, std::optional fd_mode); /** * Socket factory. Defaults to `CreateSockTCP()`, but can be overridden by unit tests. */ -extern std::function(const CService&)> CreateSock; +extern std::function(const CService&, SocketEventsMode, std::optional)> CreateSock; /** * Try to connect to the specified service on the specified socket. diff --git a/src/test/fuzz/fuzz.cpp b/src/test/fuzz/fuzz.cpp index e093f80cee149..d47a99b05a0a7 100644 --- a/src/test/fuzz/fuzz.cpp +++ b/src/test/fuzz/fuzz.cpp @@ -65,7 +65,7 @@ static TypeTestOneInput* g_test_one_input{nullptr}; void initialize() { // Terminate immediately if a fuzzing harness ever tries to create a TCP socket. - CreateSock = [](const CService&) -> std::unique_ptr { std::terminate(); }; + CreateSock = [](const CService&, SocketEventsMode, std::optional) -> std::unique_ptr { std::terminate(); }; // Terminate immediately if a fuzzing harness ever tries to perform a DNS lookup. g_dns_lookup = [](const std::string& name, bool allow_lookup) { diff --git a/src/test/fuzz/i2p.cpp b/src/test/fuzz/i2p.cpp index fb6d23aca5a34..1ba2a4efee42d 100644 --- a/src/test/fuzz/i2p.cpp +++ b/src/test/fuzz/i2p.cpp @@ -23,7 +23,7 @@ FUZZ_TARGET_INIT(i2p, initialize_i2p) // Mock CreateSock() to create FuzzedSock. auto CreateSockOrig = CreateSock; - CreateSock = [&fuzzed_data_provider](const CService&) { + CreateSock = [&fuzzed_data_provider](const CService&, SocketEventsMode, std::optional) { return std::make_unique(fuzzed_data_provider); }; diff --git a/src/test/i2p_tests.cpp b/src/test/i2p_tests.cpp index 79b34931d3f47..a761432ee5451 100644 --- a/src/test/i2p_tests.cpp +++ b/src/test/i2p_tests.cpp @@ -23,7 +23,7 @@ BOOST_AUTO_TEST_CASE(unlimited_recv) auto CreateSockOrig = CreateSock; // Mock CreateSock() to create MockSock. - CreateSock = [](const CService&) { + CreateSock = [](const CService&, SocketEventsMode, std::optional) { return std::make_unique(std::string(i2p::sam::MAX_MSG_SIZE + 1, 'a')); }; diff --git a/src/util/sock.cpp b/src/util/sock.cpp index 1d12669652d69..e46f5e2b1268b 100644 --- a/src/util/sock.cpp +++ b/src/util/sock.cpp @@ -30,12 +30,32 @@ static inline bool IOErrorIsPermanent(int err) Sock::Sock() : m_socket(INVALID_SOCKET) {} -Sock::Sock(SOCKET s) : m_socket(s) {} +Sock::Sock(SOCKET s, SocketEventsMode event_mode, std::optional fd_mode) + : m_socket(s), + m_event_mode(event_mode), + m_fd_mode(fd_mode) +{ + // Do not allow for the *possibility* of intentionally setting up a valid socket + // with an unknown event mode. + if (s != INVALID_SOCKET) { + assert(m_event_mode != SocketEventsMode::Unknown); + } + + // If we are using {epoll, kqueue} socket event modes, there must exist a file + // descriptor for manipulating the socket with the {epoll, kqueue} instance + if (m_event_mode == SocketEventsMode::EPoll || m_event_mode == SocketEventsMode::KQueue) { + assert(m_fd_mode != std::nullopt && m_fd_mode != INVALID_SOCKET); + } +} Sock::Sock(Sock&& other) { m_socket = other.m_socket; + m_event_mode = other.m_event_mode; + m_fd_mode = other.m_fd_mode; other.m_socket = INVALID_SOCKET; + other.m_event_mode = SocketEventsMode::Unknown; + other.m_fd_mode = std::nullopt; } Sock::~Sock() { Reset(); } @@ -44,7 +64,11 @@ Sock& Sock::operator=(Sock&& other) { Reset(); m_socket = other.m_socket; + m_event_mode = other.m_event_mode; + m_fd_mode = other.m_fd_mode; other.m_socket = INVALID_SOCKET; + other.m_event_mode = SocketEventsMode::Unknown; + other.m_fd_mode = std::nullopt; return *this; } @@ -54,6 +78,8 @@ SOCKET Sock::Release() { const SOCKET s = m_socket; m_socket = INVALID_SOCKET; + m_event_mode = SocketEventsMode::Unknown; + m_fd_mode = std::nullopt; return s; } @@ -97,7 +123,7 @@ std::unique_ptr Sock::Accept(sockaddr* addr, socklen_t* addr_len) const const auto socket = accept(m_socket, addr, addr_len); if (socket != ERR) { try { - sock = std::make_unique(socket); + sock = std::make_unique(socket, m_event_mode, m_fd_mode); } catch (const std::exception&) { #ifdef WIN32 closesocket(socket); diff --git a/src/util/sock.h b/src/util/sock.h index 4c18e71e0cf0e..db7b23f72861a 100644 --- a/src/util/sock.h +++ b/src/util/sock.h @@ -11,6 +11,7 @@ #include #include +#include #include /** @@ -82,7 +83,7 @@ class Sock /** * Take ownership of an existent socket. */ - explicit Sock(SOCKET s); + explicit Sock(SOCKET s, SocketEventsMode event_mode = SocketEventsMode::Select, std::optional fd_mode = std::nullopt); /** * Copy constructor, disabled because closing the same socket twice is undesirable. @@ -260,6 +261,10 @@ class Sock * Contained socket. `INVALID_SOCKET` designates the object is empty. */ SOCKET m_socket; + /* Flag for storing socket event mode */ + SocketEventsMode m_event_mode{SocketEventsMode::Unknown}; + /* Optional containing file descriptor for applicable event modes */ + std::optional m_fd_mode{std::nullopt}; }; /** Return readable error string for a network error code */ From 0979b067be35a204dcd1866859171bd699fd18f8 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 14 May 2024 18:43:13 +0000 Subject: [PATCH 2/8] net: make `Sock::Wait()` subject to selection by `-socketevents` --- src/util/sock.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- src/util/sock.h | 4 ++++ 2 files changed, 43 insertions(+), 2 deletions(-) diff --git a/src/util/sock.cpp b/src/util/sock.cpp index e46f5e2b1268b..3dbdf01a055da 100644 --- a/src/util/sock.cpp +++ b/src/util/sock.cpp @@ -153,7 +153,41 @@ int Sock::GetSockName(sockaddr* name, socklen_t* name_len) const bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const { + std::string debug_str; + + switch (m_event_mode) + { +#ifdef USE_POLL + case SocketEventsMode::Poll: + return WaitPoll(timeout, requested, occurred); +#endif /* USE_POLL */ + case SocketEventsMode::Select: + return WaitSelect(timeout, requested, occurred); + case SocketEventsMode::EPoll: + debug_str += "Unimplemented for epoll, falling back on "; + break; + case SocketEventsMode::KQueue: + debug_str += "Unimplemented for kqueue, falling back on "; + break; + default: + assert(false); + } +#ifdef USE_POLL + debug_str += "poll"; +#else + debug_str += "select"; +#endif /* USE_POLL*/ + LogPrintf("%s\n", debug_str); +#ifdef USE_POLL + return WaitPoll(timeout, requested, occurred); +#else + return WaitSelect(timeout, requested, occurred); +#endif /* USE_POLL */ +} + #ifdef USE_POLL +bool Sock::WaitPoll(std::chrono::milliseconds timeout, Event requested, Event* occurred) const +{ pollfd fd; fd.fd = m_socket; fd.events = 0; @@ -179,7 +213,11 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occur } return true; -#else +} +#endif /* USE_POLL */ + +bool Sock::WaitSelect(std::chrono::milliseconds timeout, Event requested, Event* occurred) const +{ if (!IsSelectableSocket(m_socket)) { return false; } @@ -214,7 +252,6 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occur } return true; -#endif /* USE_POLL */ } void Sock::SendComplete(const std::string& data, diff --git a/src/util/sock.h b/src/util/sock.h index db7b23f72861a..b04e7e24f1688 100644 --- a/src/util/sock.h +++ b/src/util/sock.h @@ -217,6 +217,10 @@ class Sock [[nodiscard]] virtual bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const; +#ifdef USE_POLL + bool WaitPoll(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const; +#endif /* USE_POLL */ + bool WaitSelect(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const; /* Higher level, convenience, methods. These may throw. */ From 4baae2048ac7857c6f053b65515a9b9a2e268617 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 30 Apr 2024 19:11:33 +0000 Subject: [PATCH 3/8] refactor: clean up SocketWait{Epoll, Kqueue} logic --- src/net.cpp | 53 +++++++++++++++++++++++++++-------------------- src/util/sock.h | 1 + src/util/time.cpp | 13 ++++++++++++ src/util/time.h | 2 ++ 4 files changed, 46 insertions(+), 23 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 3c157898a050f..6584d117bf664 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2286,32 +2286,31 @@ void CConnman::SocketEventsKqueue(std::set& recv_set, std::set& error_set, bool only_poll) { - const size_t maxEvents = 64; - struct kevent events[maxEvents]; - - struct timespec timeout; - timeout.tv_sec = only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS / 1000; - timeout.tv_nsec = (only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS % 1000) * 1000 * 1000; + std::array events; + struct timespec timeout = MillisToTimespec(only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS); int n{-1}; - ToggleWakeupPipe([&](){n = kevent(Assert(m_edge_trig_events)->GetFileDescriptor(), nullptr, 0, events, maxEvents, &timeout);}); + ToggleWakeupPipe([&](){ + n = kevent(Assert(m_edge_trig_events)->GetFileDescriptor(), nullptr, 0, events.data(), events.size(), &timeout); + }); if (n == -1) { LogPrintf("kevent wait error\n"); - } else if (n > 0) { - for (int i = 0; i < n; i++) { - auto& event = events[i]; - if ((event.flags & EV_ERROR) || (event.flags & EV_EOF)) { - error_set.insert((SOCKET)event.ident); - continue; - } + return; + } - if (event.filter == EVFILT_READ) { - recv_set.insert((SOCKET)event.ident); - } + for (int i = 0; i < n; i++) { + auto& event = events[i]; + if ((event.flags & EV_ERROR) || (event.flags & EV_EOF)) { + error_set.insert((SOCKET)event.ident); + continue; + } - if (event.filter == EVFILT_WRITE) { - send_set.insert((SOCKET)event.ident); - } + if (event.filter == EVFILT_READ) { + recv_set.insert((SOCKET)event.ident); + } + + if (event.filter == EVFILT_WRITE) { + send_set.insert((SOCKET)event.ident); } } } @@ -2323,11 +2322,19 @@ void CConnman::SocketEventsEpoll(std::set& recv_set, std::set& error_set, bool only_poll) { - const size_t maxEvents = 64; - epoll_event events[maxEvents]; + std::array events; int n{-1}; - ToggleWakeupPipe([&](){n = epoll_wait(Assert(m_edge_trig_events)->GetFileDescriptor(), events, maxEvents, only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS);}); + ToggleWakeupPipe([&](){ + n = epoll_wait(Assert(m_edge_trig_events)->GetFileDescriptor(), events.data(), events.size(), + only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS); + }); + + if (n == -1) { + LogPrintf("epoll_wait error\n"); + return; + } + for (int i = 0; i < n; i++) { auto& e = events[i]; if((e.events & EPOLLERR) || (e.events & EPOLLHUP)) { diff --git a/src/util/sock.h b/src/util/sock.h index b04e7e24f1688..38885c2c1fd7f 100644 --- a/src/util/sock.h +++ b/src/util/sock.h @@ -19,6 +19,7 @@ * It will take up until this time to break off in case of an interruption. */ static constexpr auto MAX_WAIT_FOR_IO = 1s; +static constexpr size_t MAX_EVENTS = 64; enum class SocketEventsMode : int8_t { Select = 0, diff --git a/src/util/time.cpp b/src/util/time.cpp index 14183d540d8bd..c141d1fdfb0d4 100644 --- a/src/util/time.cpp +++ b/src/util/time.cpp @@ -173,6 +173,14 @@ int64_t ParseISO8601DateTime(const std::string& str) return (ptime - epoch).total_seconds(); } +struct timespec MillisToTimespec(int64_t nTimeout) +{ + struct timespec timeout; + timeout.tv_sec = nTimeout / 1000; + timeout.tv_nsec = (nTimeout % 1000) * 1000 * 1000; + return timeout; +} + struct timeval MillisToTimeval(int64_t nTimeout) { struct timeval timeout; @@ -181,6 +189,11 @@ struct timeval MillisToTimeval(int64_t nTimeout) return timeout; } +struct timespec MillisToTimespec(std::chrono::milliseconds ms) +{ + return MillisToTimespec(count_milliseconds(ms)); +} + struct timeval MillisToTimeval(std::chrono::milliseconds ms) { return MillisToTimeval(count_milliseconds(ms)); diff --git a/src/util/time.h b/src/util/time.h index 36300b29fd11a..c3cbd00f4feae 100644 --- a/src/util/time.h +++ b/src/util/time.h @@ -95,11 +95,13 @@ int64_t ParseISO8601DateTime(const std::string& str); * Convert milliseconds to a struct timeval for e.g. select. */ struct timeval MillisToTimeval(int64_t nTimeout); +struct timespec MillisToTimespec(int64_t nTimeout); /** * Convert milliseconds to a struct timeval for e.g. select. */ struct timeval MillisToTimeval(std::chrono::milliseconds ms); +struct timespec MillisToTimespec(std::chrono::milliseconds ms); /** Sanity check epoch match normal Unix epoch */ bool ChronoSanityCheck(); From 8d49a7e796636c9f59aa42ff80f2b20b3690ec83 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Thu, 26 Sep 2024 21:05:03 +0000 Subject: [PATCH 4/8] merge bitcoin#24356: replace CConnman::SocketEvents() with mockable Sock::WaitMany() Co-authored-by: UdjinM6 --- src/i2p.cpp | 4 +- src/net.cpp | 292 +++++++++-------------------------------- src/net.h | 49 +------ src/test/fuzz/util.cpp | 9 ++ src/test/fuzz/util.h | 2 + src/test/util/net.h | 9 ++ src/util/sock.cpp | 130 +++++++++++------- src/util/sock.h | 72 ++++++++-- 8 files changed, 237 insertions(+), 330 deletions(-) diff --git a/src/i2p.cpp b/src/i2p.cpp index 7036642039955..a9b4595ecf3e5 100644 --- a/src/i2p.cpp +++ b/src/i2p.cpp @@ -162,8 +162,8 @@ bool Session::Accept(Connection& conn) throw std::runtime_error("wait on socket failed"); } - if ((occurred & Sock::RECV) == 0) { - // Timeout, no incoming connections within MAX_WAIT_FOR_IO. + if (occurred == 0) { + // Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO. continue; } diff --git a/src/net.cpp b/src/net.cpp index 6584d117bf664..dd2be6eae3cec 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -54,10 +54,6 @@ #include #endif -#ifdef USE_POLL -#include -#endif - #ifdef USE_EPOLL #include #endif @@ -2241,13 +2237,12 @@ bool CConnman::InactivityCheck(const CNode& node) const return false; } -bool CConnman::GenerateSelectSet(const std::vector& nodes, - std::set& recv_set, - std::set& send_set, - std::set& error_set) +Sock::EventsPerSock CConnman::GenerateWaitSockets(Span nodes) { + Sock::EventsPerSock events_per_sock; + for (const ListenSocket& hListenSocket : vhListenSocket) { - recv_set.insert(hListenSocket.sock->Get()); + events_per_sock.emplace(hListenSocket.sock->Get(), Sock::Events{Sock::RECV}); } for (CNode* pnode : nodes) { @@ -2259,13 +2254,15 @@ bool CConnman::GenerateSelectSet(const std::vector& nodes, continue; } - error_set.insert(pnode->m_sock->Get()); + Sock::Event requested{0}; if (select_send) { - send_set.insert(pnode->m_sock->Get()); + requested |= Sock::SEND; } if (select_recv) { - recv_set.insert(pnode->m_sock->Get()); + requested |= Sock::RECV; } + + events_per_sock.emplace(pnode->m_sock->Get(), Sock::Events{requested}); } if (m_wakeup_pipe) { @@ -2274,10 +2271,10 @@ bool CConnman::GenerateSelectSet(const std::vector& nodes, // This is currently only implemented for POSIX compliant systems. This means that Windows will fall back to // timing out after 50ms and then trying to send. This is ok as we assume that heavy-load daemons are usually // run on Linux and friends. - recv_set.insert(m_wakeup_pipe->m_pipe[0]); + events_per_sock.emplace(m_wakeup_pipe->m_pipe[0], Sock::Events{Sock::RECV}); } - return !recv_set.empty() || !send_set.empty() || !error_set.empty(); + return events_per_sock; } #ifdef USE_KQUEUE @@ -2353,173 +2350,11 @@ void CConnman::SocketEventsEpoll(std::set& recv_set, } #endif -#ifdef USE_POLL -void CConnman::SocketEventsPoll(const std::vector& nodes, - std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll) -{ - std::set recv_select_set, send_select_set, error_select_set; - if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) { - if (!only_poll) interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)); - return; - } - - std::unordered_map pollfds; - for (SOCKET socket_id : recv_select_set) { - pollfds[socket_id].fd = socket_id; - pollfds[socket_id].events |= POLLIN; - } - - for (SOCKET socket_id : send_select_set) { - pollfds[socket_id].fd = socket_id; - pollfds[socket_id].events |= POLLOUT; - } - - for (SOCKET socket_id : error_select_set) { - pollfds[socket_id].fd = socket_id; - // These flags are ignored, but we set them for clarity - pollfds[socket_id].events |= POLLERR|POLLHUP; - } - - std::vector vpollfds; - vpollfds.reserve(pollfds.size()); - for (auto it : pollfds) { - vpollfds.push_back(std::move(it.second)); - } - - int r{-1}; - ToggleWakeupPipe([&](){r = poll(vpollfds.data(), vpollfds.size(), only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS);}); - if (r < 0) { - return; - } - - if (interruptNet) return; - - for (struct pollfd pollfd_entry : vpollfds) { - if (pollfd_entry.revents & POLLIN) recv_set.insert(pollfd_entry.fd); - if (pollfd_entry.revents & POLLOUT) send_set.insert(pollfd_entry.fd); - if (pollfd_entry.revents & (POLLERR|POLLHUP)) error_set.insert(pollfd_entry.fd); - } -} -#endif - -void CConnman::SocketEventsSelect(const std::vector& nodes, - std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll) -{ - std::set recv_select_set, send_select_set, error_select_set; - if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) { - interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)); - return; - } - - // - // Find which sockets have data to receive - // - struct timeval timeout; - timeout.tv_sec = 0; - timeout.tv_usec = only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS * 1000; // frequency to poll pnode->vSend - - fd_set fdsetRecv; - fd_set fdsetSend; - fd_set fdsetError; - FD_ZERO(&fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - SOCKET hSocketMax = 0; - - for (SOCKET hSocket : recv_select_set) { - FD_SET(hSocket, &fdsetRecv); - hSocketMax = std::max(hSocketMax, hSocket); - } - - for (SOCKET hSocket : send_select_set) { - FD_SET(hSocket, &fdsetSend); - hSocketMax = std::max(hSocketMax, hSocket); - } - - for (SOCKET hSocket : error_select_set) { - FD_SET(hSocket, &fdsetError); - hSocketMax = std::max(hSocketMax, hSocket); - } - - int nSelect{-1}; - ToggleWakeupPipe([&](){nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout);}); - if (interruptNet) - return; - - if (nSelect == SOCKET_ERROR) - { - int nErr = WSAGetLastError(); - LogPrintf("socket select error %s\n", NetworkErrorString(nErr)); - for (unsigned int i = 0; i <= hSocketMax; i++) - FD_SET(i, &fdsetRecv); - FD_ZERO(&fdsetSend); - FD_ZERO(&fdsetError); - if (!interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS))) - return; - } - - for (SOCKET hSocket : recv_select_set) { - if (FD_ISSET(hSocket, &fdsetRecv)) { - recv_set.insert(hSocket); - } - } - - for (SOCKET hSocket : send_select_set) { - if (FD_ISSET(hSocket, &fdsetSend)) { - send_set.insert(hSocket); - } - } - - for (SOCKET hSocket : error_select_set) { - if (FD_ISSET(hSocket, &fdsetError)) { - error_set.insert(hSocket); - } - } -} - -void CConnman::SocketEvents(const std::vector& nodes, - std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll) -{ - switch (socketEventsMode) { -#ifdef USE_KQUEUE - case SocketEventsMode::KQueue: - SocketEventsKqueue(recv_set, send_set, error_set, only_poll); - break; -#endif -#ifdef USE_EPOLL - case SocketEventsMode::EPoll: - SocketEventsEpoll(recv_set, send_set, error_set, only_poll); - break; -#endif -#ifdef USE_POLL - case SocketEventsMode::Poll: - SocketEventsPoll(nodes, recv_set, send_set, error_set, only_poll); - break; -#endif - case SocketEventsMode::Select: - SocketEventsSelect(nodes, recv_set, send_set, error_set, only_poll); - break; - default: - assert(false); - } -} - void CConnman::SocketHandler(CMasternodeSync& mn_sync) { AssertLockNotHeld(m_total_bytes_sent_mutex); - std::set recv_set; - std::set send_set; - std::set error_set; + Sock::EventsPerSock events_per_sock; bool only_poll = [this]() { // Check if we have work to do and thus should avoid waiting for events @@ -2545,72 +2380,64 @@ void CConnman::SocketHandler(CMasternodeSync& mn_sync) { const NodesSnapshot snap{*this, /* filter = */ CConnman::AllNodes, /* shuffle = */ false}; + const auto timeout = std::chrono::milliseconds(only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS); + const bool is_lt = socketEventsMode == SocketEventsMode::Poll || socketEventsMode == SocketEventsMode::Select; + // Check for the readiness of the already connected sockets and the // listening sockets in one call ("readiness" as in poll(2) or // select(2)). If none are ready, wait for a short while and return // empty sets. - SocketEvents(snap.Nodes(), recv_set, send_set, error_set, only_poll); + events_per_sock = GenerateWaitSockets(snap.Nodes()); + if ((is_lt && events_per_sock.empty()) || !Sock::IWaitMany(socketEventsMode, timeout, events_per_sock)) { + if (is_lt) { + interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)); + } + } - // Drain the wakeup pipe - if (m_wakeup_pipe && recv_set.count(m_wakeup_pipe->m_pipe[0])) { - m_wakeup_pipe->Drain(); - } + // Drain the wakeup pipe + if (m_wakeup_pipe && events_per_sock.find(m_wakeup_pipe->m_pipe[0]) != events_per_sock.end()) { + m_wakeup_pipe->Drain(); + } // Service (send/receive) each of the already connected nodes. - SocketHandlerConnected(recv_set, send_set, error_set); + SocketHandlerConnected(events_per_sock); } // Accept new connections from listening sockets. - SocketHandlerListening(recv_set, mn_sync); + SocketHandlerListening(events_per_sock, mn_sync); } -void CConnman::SocketHandlerConnected(const std::set& recv_set, - const std::set& send_set, - const std::set& error_set) +void CConnman::SocketHandlerConnected(const Sock::EventsPerSock& events_per_sock) { AssertLockNotHeld(m_total_bytes_sent_mutex); if (interruptNet) return; - std::set vErrorNodes; - std::set vReceivableNodes; - std::set vSendableNodes; + std::set node_err_set; + std::set node_recv_set; + std::set node_send_set; { LOCK(cs_mapSocketToNode); - for (auto hSocket : error_set) { - auto it = mapSocketToNode.find(hSocket); - if (it == mapSocketToNode.end()) { - continue; + for (const auto& [sock, events] : events_per_sock) { + auto it = mapSocketToNode.find(sock); + if (it == mapSocketToNode.end()) continue; + if (events.occurred & Sock::ERR) { + it->second->AddRef(); + node_err_set.emplace(it->second); } - it->second->AddRef(); - vErrorNodes.emplace(it->second); - } - for (auto hSocket : recv_set) { - if (error_set.count(hSocket)) { - // no need to handle it twice - continue; + if (events.occurred & Sock::RECV) { + if (events.occurred & Sock::ERR) continue; + LOCK(cs_sendable_receivable_nodes); + auto jt = mapReceivableNodes.emplace(it->second->GetId(), it->second); + assert(jt.first->second == it->second); + it->second->fHasRecvData = true; } - - auto it = mapSocketToNode.find(hSocket); - if (it == mapSocketToNode.end()) { - continue; + if (events.occurred & Sock::SEND) { + LOCK(cs_sendable_receivable_nodes); + auto jt = mapSendableNodes.emplace(it->second->GetId(), it->second); + assert(jt.first->second == it->second); + it->second->fCanSendData = true; } - - LOCK(cs_sendable_receivable_nodes); - auto jt = mapReceivableNodes.emplace(it->second->GetId(), it->second); - assert(jt.first->second == it->second); - it->second->fHasRecvData = true; - } - for (auto hSocket : send_set) { - auto it = mapSocketToNode.find(hSocket); - if (it == mapSocketToNode.end()) { - continue; - } - - LOCK(cs_sendable_receivable_nodes); - auto jt = mapSendableNodes.emplace(it->second->GetId(), it->second); - assert(jt.first->second == it->second); - it->second->fCanSendData = true; } // collect nodes that have a receivable socket @@ -2634,7 +2461,7 @@ void CConnman::SocketHandlerConnected(const std::set& recv_set, const bool queue_is_empty{to_send.empty() && !more}; if (!it->second->fPauseRecv && !it->second->fDisconnect && queue_is_empty) { it->second->AddRef(); - vReceivableNodes.emplace(it->second); + node_recv_set.emplace(it->second); } ++it; } @@ -2654,14 +2481,14 @@ void CConnman::SocketHandlerConnected(const std::set& recv_set, } else { if (it->second->fCanSendData) { it->second->AddRef(); - vSendableNodes.emplace(it->second); + node_send_set.emplace(it->second); } ++it; } } } - for (CNode* pnode : vSendableNodes) { + for (CNode* pnode : node_send_set) { if (interruptNet) { break; } @@ -2678,13 +2505,13 @@ void CConnman::SocketHandlerConnected(const std::set& recv_set, // sending actually succeeded to make sure progress is always made; otherwise a // deadlock would be possible when both sides have data to send, but neither is // receiving. - if (data_left && vReceivableNodes.erase(pnode)) { + if (data_left && node_recv_set.erase(pnode)) { pnode->Release(); } } } - for (CNode* pnode : vErrorNodes) + for (CNode* pnode : node_err_set) { if (interruptNet) { break; @@ -2693,7 +2520,7 @@ void CConnman::SocketHandlerConnected(const std::set& recv_set, SocketRecvData(pnode); } - for (CNode* pnode : vReceivableNodes) + for (CNode* pnode : node_recv_set) { if (interruptNet) { break; @@ -2705,13 +2532,13 @@ void CConnman::SocketHandlerConnected(const std::set& recv_set, SocketRecvData(pnode); } - for (auto& node : vErrorNodes) { + for (auto& node : node_err_set) { node->Release(); } - for (auto& node : vReceivableNodes) { + for (auto& node : node_recv_set) { node->Release(); } - for (auto& node : vSendableNodes) { + for (auto& node : node_send_set) { node->Release(); } @@ -2734,13 +2561,14 @@ void CConnman::SocketHandlerConnected(const std::set& recv_set, } } -void CConnman::SocketHandlerListening(const std::set& recv_set, CMasternodeSync& mn_sync) +void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock, CMasternodeSync& mn_sync) { for (const ListenSocket& listen_socket : vhListenSocket) { if (interruptNet) { return; } - if (recv_set.count(listen_socket.sock->Get()) > 0) { + const auto it = events_per_sock.find(listen_socket.sock->Get()); + if (it != events_per_sock.end() && (it->second.occurred & Sock::RECV)) { AcceptConnection(listen_socket, mn_sync); } } diff --git a/src/net.h b/src/net.h index 03a1487045bb0..b9ec00442052a 100644 --- a/src/net.h +++ b/src/net.h @@ -1575,30 +1575,9 @@ friend class CNode; /** * Generate a collection of sockets to check for IO readiness. * @param[in] nodes Select from these nodes' sockets. - * @param[out] recv_set Sockets to check for read readiness. - * @param[out] send_set Sockets to check for write readiness. - * @param[out] error_set Sockets to check for errors. - * @return true if at least one socket is to be checked (the returned set is not empty) + * @return sockets to check for readiness */ - bool GenerateSelectSet(const std::vector& nodes, - std::set& recv_set, - std::set& send_set, - std::set& error_set); - - /** - * Check which sockets are ready for IO. - * @param[in] nodes Select from these nodes' sockets (in supported event methods). - * @param[in] only_poll Permit zero timeout polling - * @param[out] recv_set Sockets which are ready for read. - * @param[out] send_set Sockets which are ready for write. - * @param[out] error_set Sockets which have errors. - * This calls `GenerateSelectSet()` to gather a list of sockets to check. - */ - void SocketEvents(const std::vector& nodes, - std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll); + Sock::EventsPerSock GenerateWaitSockets(Span nodes); #ifdef USE_KQUEUE void SocketEventsKqueue(std::set& recv_set, @@ -1612,18 +1591,6 @@ friend class CNode; std::set& error_set, bool only_poll); #endif -#ifdef USE_POLL - void SocketEventsPoll(const std::vector& nodes, - std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll); -#endif - void SocketEventsSelect(const std::vector& nodes, - std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll); /** * Check connected and listening sockets for IO readiness and process them accordingly. @@ -1632,20 +1599,16 @@ friend class CNode; /** * Do the read/write for connected sockets that are ready for IO. - * @param[in] recv_set Sockets that are ready for read. - * @param[in] send_set Sockets that are ready for send. - * @param[in] error_set Sockets that have an exceptional condition (error). + * @param[in] events_per_sock Sockets that are ready for IO. */ - void SocketHandlerConnected(const std::set& recv_set, - const std::set& send_set, - const std::set& error_set) + void SocketHandlerConnected(const Sock::EventsPerSock& events_per_sock) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc); /** * Accept incoming connections, one from each read-ready listening socket. - * @param[in] recv_set Sockets that are ready for read. + * @param[in] events_per_sock Sockets that are ready for IO. */ - void SocketHandlerListening(const std::set& recv_set, CMasternodeSync& mn_sync) + void SocketHandlerListening(const Sock::EventsPerSock& events_per_sock, CMasternodeSync& mn_sync) EXCLUSIVE_LOCKS_REQUIRED(!mutexMsgProc); void ThreadSocketHandler(CMasternodeSync& mn_sync) EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc); diff --git a/src/test/fuzz/util.cpp b/src/test/fuzz/util.cpp index c30ff2d3e92bb..7a4500622f8b1 100644 --- a/src/test/fuzz/util.cpp +++ b/src/test/fuzz/util.cpp @@ -273,6 +273,15 @@ bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event* return true; } +bool FuzzedSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const +{ + for (auto& [sock, events] : events_per_sock) { + (void)sock; + events.occurred = m_fuzzed_data_provider.ConsumeBool() ? events.requested : 0; + } + return true; +} + bool FuzzedSock::IsConnected(std::string& errmsg) const { if (m_fuzzed_data_provider.ConsumeBool()) { diff --git a/src/test/fuzz/util.h b/src/test/fuzz/util.h index e49c8130b895d..482baee3a5a2f 100644 --- a/src/test/fuzz/util.h +++ b/src/test/fuzz/util.h @@ -81,6 +81,8 @@ class FuzzedSock : public Sock bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override; + bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override; + bool IsConnected(std::string& errmsg) const override; }; diff --git a/src/test/util/net.h b/src/test/util/net.h index 893ade2430d8e..9fe2f9af30794 100644 --- a/src/test/util/net.h +++ b/src/test/util/net.h @@ -182,6 +182,15 @@ class StaticContentsSock : public Sock return true; } + bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override + { + for (auto& [sock, events] : events_per_sock) { + (void)sock; + events.occurred = events.requested; + } + return true; + } + private: const std::string m_contents; mutable size_t m_consumed; diff --git a/src/util/sock.cpp b/src/util/sock.cpp index 3dbdf01a055da..dc276668e1358 100644 --- a/src/util/sock.cpp +++ b/src/util/sock.cpp @@ -152,17 +152,37 @@ int Sock::GetSockName(sockaddr* name, socklen_t* name_len) const } bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred) const +{ + EventsPerSock events_per_sock{std::make_pair(m_socket, Events{requested})}; + + if (!WaitMany(timeout, events_per_sock)) { + return false; + } + + if (occurred != nullptr) { + *occurred = events_per_sock.begin()->second.occurred; + } + + return true; +} + +bool Sock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const +{ + return IWaitMany(m_event_mode, timeout, events_per_sock); +} + +bool Sock::IWaitMany(SocketEventsMode event_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) { std::string debug_str; - switch (m_event_mode) + switch (event_mode) { #ifdef USE_POLL case SocketEventsMode::Poll: - return WaitPoll(timeout, requested, occurred); + return WaitManyPoll(timeout, events_per_sock); #endif /* USE_POLL */ case SocketEventsMode::Select: - return WaitSelect(timeout, requested, occurred); + return WaitManySelect(timeout, events_per_sock); case SocketEventsMode::EPoll: debug_str += "Unimplemented for epoll, falling back on "; break; @@ -179,75 +199,95 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occur #endif /* USE_POLL*/ LogPrintf("%s\n", debug_str); #ifdef USE_POLL - return WaitPoll(timeout, requested, occurred); + return WaitManyPoll(timeout, events_per_sock); #else - return WaitSelect(timeout, requested, occurred); + return WaitManySelect(timeout, events_per_sock); #endif /* USE_POLL */ } #ifdef USE_POLL -bool Sock::WaitPoll(std::chrono::milliseconds timeout, Event requested, Event* occurred) const +bool Sock::WaitManyPoll(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) { - pollfd fd; - fd.fd = m_socket; - fd.events = 0; - if (requested & RECV) { - fd.events |= POLLIN; - } - if (requested & SEND) { - fd.events |= POLLOUT; + std::vector pfds; + for (const auto& [socket, events] : events_per_sock) { + pfds.emplace_back(); + auto& pfd = pfds.back(); + pfd.fd = socket; + if (events.requested & RECV) { + pfd.events |= POLLIN; + } + if (events.requested & SEND) { + pfd.events |= POLLOUT; + } } - if (poll(&fd, 1, count_milliseconds(timeout)) == SOCKET_ERROR) { + if (poll(pfds.data(), pfds.size(), count_milliseconds(timeout)) == SOCKET_ERROR) { return false; } - if (occurred != nullptr) { - *occurred = 0; - if (fd.revents & POLLIN) { - *occurred |= RECV; + assert(pfds.size() == events_per_sock.size()); + size_t i{0}; + for (auto& [socket, events] : events_per_sock) { + assert(socket == static_cast(pfds[i].fd)); + events.occurred = 0; + if (pfds[i].revents & POLLIN) { + events.occurred |= RECV; } - if (fd.revents & POLLOUT) { - *occurred |= SEND; + if (pfds[i].revents & POLLOUT) { + events.occurred |= SEND; } + if (pfds[i].revents & (POLLERR | POLLHUP)) { + events.occurred |= ERR; + } + ++i; } return true; } #endif /* USE_POLL */ -bool Sock::WaitSelect(std::chrono::milliseconds timeout, Event requested, Event* occurred) const +bool Sock::WaitManySelect(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) { - if (!IsSelectableSocket(m_socket)) { - return false; - } - - fd_set fdset_recv; - fd_set fdset_send; - FD_ZERO(&fdset_recv); - FD_ZERO(&fdset_send); - - if (requested & RECV) { - FD_SET(m_socket, &fdset_recv); - } - - if (requested & SEND) { - FD_SET(m_socket, &fdset_send); + fd_set recv; + fd_set send; + fd_set err; + FD_ZERO(&recv); + FD_ZERO(&send); + FD_ZERO(&err); + SOCKET socket_max{0}; + + for (const auto& [sock, events] : events_per_sock) { + const auto& s = sock; + if (!IsSelectableSocket(s)) { + return false; + } + if (events.requested & RECV) { + FD_SET(s, &recv); + } + if (events.requested & SEND) { + FD_SET(s, &send); + } + FD_SET(s, &err); + socket_max = std::max(socket_max, s); } - timeval timeout_struct = MillisToTimeval(timeout); + timeval tv = MillisToTimeval(timeout); - if (select(m_socket + 1, &fdset_recv, &fdset_send, nullptr, &timeout_struct) == SOCKET_ERROR) { + if (select(socket_max + 1, &recv, &send, &err, &tv) == SOCKET_ERROR) { return false; } - if (occurred != nullptr) { - *occurred = 0; - if (FD_ISSET(m_socket, &fdset_recv)) { - *occurred |= RECV; + for (auto& [sock, events] : events_per_sock) { + const auto& s = sock; + events.occurred = 0; + if (FD_ISSET(s, &recv)) { + events.occurred |= RECV; + } + if (FD_ISSET(s, &send)) { + events.occurred |= SEND; } - if (FD_ISSET(m_socket, &fdset_send)) { - *occurred |= SEND; + if (FD_ISSET(s, &err)) { + events.occurred |= ERR; } } diff --git a/src/util/sock.h b/src/util/sock.h index 38885c2c1fd7f..b23f4aad67736 100644 --- a/src/util/sock.h +++ b/src/util/sock.h @@ -13,6 +13,7 @@ #include #include #include +#include /** * Maximum time to wait for I/O readiness. @@ -199,29 +200,84 @@ class Sock /** * If passed to `Wait()`, then it will wait for readiness to read from the socket. */ - static constexpr Event RECV = 0b01; + static constexpr Event RECV = 0b001; /** * If passed to `Wait()`, then it will wait for readiness to send to the socket. */ - static constexpr Event SEND = 0b10; + static constexpr Event SEND = 0b010; + + /** + * Ignored if passed to `Wait()`, but could be set in the occurred events if an + * exceptional condition has occurred on the socket or if it has been disconnected. + */ + static constexpr Event ERR = 0b100; /** * Wait for readiness for input (recv) or output (send). * @param[in] timeout Wait this much for at least one of the requested events to occur. * @param[in] requested Wait for those events, bitwise-or of `RECV` and `SEND`. - * @param[out] occurred If not nullptr and `true` is returned, then upon return this - * indicates which of the requested events occurred. A timeout is indicated by return - * value of `true` and `occurred` being set to 0. - * @return true on success and false otherwise + * @param[out] occurred If not nullptr and the function returns `true`, then this + * indicates which of the requested events occurred (`ERR` will be added, even if + * not requested, if an exceptional event occurs on the socket). + * A timeout is indicated by return value of `true` and `occurred` being set to 0. + * @return true on success (or timeout, if `occurred` of 0 is returned), false otherwise */ [[nodiscard]] virtual bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const; + /** + * Auxiliary requested/occurred events to wait for in `WaitMany()`. + */ + struct Events { + explicit Events(Event req) : requested{req}, occurred{0} {} + Event requested; + Event occurred; + }; + + /** + * On which socket to wait for what events in `WaitMany()`. + * + * Bitcoin: + * The `shared_ptr` is copied into the map to ensure that the `Sock` object + * is not destroyed (its destructor would close the underlying socket). + * If this happens shortly before or after we call `poll(2)` and a new + * socket gets created under the same file descriptor number then the report + * from `WaitMany()` will be bogus. + * + * Dash: + * The raw `SOCKET` file descriptor is copied into the map (generally taken from + * Sock::Get()) to allow sockets managed by external logic (e.g. WakeupPipes) to + * be used without wrapping it into a Sock object and risk handing control over. + */ + using EventsPerSock = std::unordered_map; + + /** + * Same as `Wait()`, but wait on many sockets within the same timeout. + * @param[in] timeout Wait this long for at least one of the requested events to occur. + * @param[in,out] events_per_sock Wait for the requested events on these sockets and set + * `occurred` for the events that actually occurred. + * @return true on success (or timeout, if all `what[].occurred` are returned as 0), + * false otherwise + */ + [[nodiscard]] virtual bool WaitMany(std::chrono::milliseconds timeout, + EventsPerSock& events_per_sock) const; + + /** + * As an EventsPerSock map no longer contains a Sock object (it now contains the raw SOCKET file + * descriptor), we lose access to all the logic implemented in Sock. Except that as WaitMany + * doesn't interact with the raw socket stored within Sock, it can be safely declared as static + * and we can pass all other parameters as arguments as it should ordinarily remain the same for + * entire runtime duration of the program. + * + * This doesn't apply to Sock::Wait(), as it populates an EventsPerSock map with its own raw + * socket before passing it to WaitMany. + */ + static bool IWaitMany(SocketEventsMode event_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); #ifdef USE_POLL - bool WaitPoll(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const; + static bool WaitManyPoll(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); #endif /* USE_POLL */ - bool WaitSelect(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const; + static bool WaitManySelect(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); /* Higher level, convenience, methods. These may throw. */ From 6ffc83292dbf153f6020e93748ebd8730ce521b4 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Wed, 29 May 2024 21:39:44 +0000 Subject: [PATCH 5/8] net: implement `WaitMany` variants for {`epoll`, `kqueue`} Co-authored-by: UdjinM6 --- src/net.cpp | 2 +- src/util/sock.cpp | 112 +++++++++++++++++++++++++++++++++++++++------- src/util/sock.h | 11 ++++- 3 files changed, 107 insertions(+), 18 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index dd2be6eae3cec..9dc5f0514b5f5 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -2388,7 +2388,7 @@ void CConnman::SocketHandler(CMasternodeSync& mn_sync) // select(2)). If none are ready, wait for a short while and return // empty sets. events_per_sock = GenerateWaitSockets(snap.Nodes()); - if ((is_lt && events_per_sock.empty()) || !Sock::IWaitMany(socketEventsMode, timeout, events_per_sock)) { + if ((is_lt && events_per_sock.empty()) || !Sock::IWaitMany(socketEventsMode, GetModeFileDescriptor(), timeout, events_per_sock)) { if (is_lt) { interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)); } diff --git a/src/util/sock.cpp b/src/util/sock.cpp index dc276668e1358..ac8782db8b806 100644 --- a/src/util/sock.cpp +++ b/src/util/sock.cpp @@ -19,6 +19,14 @@ #include #endif +#ifdef USE_EPOLL +#include +#endif + +#ifdef USE_KQUEUE +#include +#endif + #ifdef USE_POLL #include #endif @@ -155,7 +163,9 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occur { EventsPerSock events_per_sock{std::make_pair(m_socket, Events{requested})}; - if (!WaitMany(timeout, events_per_sock)) { + /* We need to specify that we only want level-triggered modes used because we are expecting + a direct correlation between the events reported and the one socket we are querying */ + if (!IWaitMany(m_event_mode, m_fd_mode, timeout, events_per_sock, /* lt_only = */ true)) { return false; } @@ -168,13 +178,12 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occur bool Sock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const { - return IWaitMany(m_event_mode, timeout, events_per_sock); + return IWaitMany(m_event_mode, m_fd_mode, timeout, events_per_sock); } -bool Sock::IWaitMany(SocketEventsMode event_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) +bool Sock::IWaitMany(SocketEventsMode event_mode, std::optional fd_mode, std::chrono::milliseconds timeout, + EventsPerSock& events_per_sock, bool lt_only) { - std::string debug_str; - switch (event_mode) { #ifdef USE_POLL @@ -183,21 +192,25 @@ bool Sock::IWaitMany(SocketEventsMode event_mode, std::chrono::milliseconds time #endif /* USE_POLL */ case SocketEventsMode::Select: return WaitManySelect(timeout, events_per_sock); +#ifdef USE_EPOLL case SocketEventsMode::EPoll: - debug_str += "Unimplemented for epoll, falling back on "; - break; + /* If we explictly need to use a level triggered mode, skip below to fallback */ + if (lt_only) break; + assert(fd_mode); + return WaitManyEPoll(fd_mode.value(), timeout, events_per_sock); +#endif /* USE_EPOLL */ +#ifdef USE_KQUEUE case SocketEventsMode::KQueue: - debug_str += "Unimplemented for kqueue, falling back on "; - break; + /* If we explictly need to use a level triggered mode, skip below to fallback */ + if (lt_only) break; + assert(fd_mode); + return WaitManyKQueue(fd_mode.value(), timeout, events_per_sock); +#endif /* USE_KQUEUE */ default: assert(false); } -#ifdef USE_POLL - debug_str += "poll"; -#else - debug_str += "select"; -#endif /* USE_POLL*/ - LogPrintf("%s\n", debug_str); + /* We should only be here if we need a level-triggered mode but the instance is edge-triggered instead */ + assert(lt_only); #ifdef USE_POLL return WaitManyPoll(timeout, events_per_sock); #else @@ -205,6 +218,75 @@ bool Sock::IWaitMany(SocketEventsMode event_mode, std::chrono::milliseconds time #endif /* USE_POLL */ } +#ifdef USE_EPOLL +bool Sock::WaitManyEPoll(int fd_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) +{ + std::array events; + + int ret = epoll_wait(fd_mode, events.data(), events.size(), count_milliseconds(timeout)); + if (ret == SOCKET_ERROR) { + return false; + } + + /* Events reported do not correspond to sockets requested in edge-triggered modes, we will clear the + entire map before populating it with our events data. */ + events_per_sock.clear(); + + for (int idx = 0; idx < ret; idx++) { + auto& ev = events[idx]; + Event occurred = 0; + if (ev.events & (EPOLLERR | EPOLLHUP)) { + occurred |= ERR; + } else { + if (ev.events & EPOLLIN) { + occurred |= RECV; + } + if (ev.events & EPOLLOUT) { + occurred |= SEND; + } + } + events_per_sock.emplace(static_cast(ev.data.fd), Sock::Events{occurred, occurred}); + } + + return true; +} +#endif /* USE_EPOLL */ + +#ifdef USE_KQUEUE +bool Sock::WaitManyKQueue(int fd_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) +{ + std::array events; + struct timespec ts = MillisToTimespec(timeout); + + int ret = kevent(fd_mode, nullptr, 0, events.data(), events.size(), &ts); + if (ret == SOCKET_ERROR) { + return false; + } + + /* Events reported do not correspond to sockets requested in edge-triggered modes, we will clear the + entire map before populating it with our events data. */ + events_per_sock.clear(); + + for (int idx = 0; idx < ret; idx++) { + auto& ev = events[idx]; + Event occurred = 0; + if (ev.flags & (EV_ERROR | EV_EOF)) { + occurred |= ERR; + } else { + if (ev.filter == EVFILT_READ) { + occurred |= RECV; + } + if (ev.filter == EVFILT_WRITE) { + occurred |= SEND; + } + } + events_per_sock.emplace(static_cast(ev.ident), Sock::Events{occurred, occurred}); + } + + return true; +} +#endif /* USE_KQUEUE */ + #ifdef USE_POLL bool Sock::WaitManyPoll(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) { diff --git a/src/util/sock.h b/src/util/sock.h index b23f4aad67736..c1123538d2216 100644 --- a/src/util/sock.h +++ b/src/util/sock.h @@ -230,7 +230,7 @@ class Sock * Auxiliary requested/occurred events to wait for in `WaitMany()`. */ struct Events { - explicit Events(Event req) : requested{req}, occurred{0} {} + explicit Events(Event req, Event ocr = 0) : requested{req}, occurred{ocr} {} Event requested; Event occurred; }; @@ -273,7 +273,14 @@ class Sock * This doesn't apply to Sock::Wait(), as it populates an EventsPerSock map with its own raw * socket before passing it to WaitMany. */ - static bool IWaitMany(SocketEventsMode event_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); + static bool IWaitMany(SocketEventsMode event_mode, std::optional fd_mode, std::chrono::milliseconds timeout, + EventsPerSock& events_per_sock, bool lt_only = false); +#ifdef USE_EPOLL + static bool WaitManyEPoll(int fd_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); +#endif /* USE_EPOLL */ +#ifdef USE_KQUEUE + static bool WaitManyKQueue(int fd_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); +#endif /* USE_KQUEUE */ #ifdef USE_POLL static bool WaitManyPoll(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); #endif /* USE_POLL */ From a604dee5e9e08471bfc7407c6a018deabb8fdf85 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Tue, 14 May 2024 18:47:00 +0000 Subject: [PATCH 6/8] net: implement `ToggleWakeupPipe` in all `WaitMany` variants `ToggleWakeupPipe` code was removed in bitcoin#24356 as part of migration from `CConnman::SocketEvents` to `Sock::WaitMany` and because `Sock` didn't have the necessary plumbing. --- src/net.cpp | 9 +++++++- src/net.h | 6 ++--- src/util/sock.cpp | 57 ++++++++++++++++++++++++++++++++--------------- src/util/sock.h | 24 +++++++++++++++----- 4 files changed, 67 insertions(+), 29 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 9dc5f0514b5f5..9e1b23ff9852f 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -608,6 +608,9 @@ CNode* CConnman::ConnectNode(CAddress addrConnect, const char *pszDest, bool fCo return nullptr; } + // Set toggle wakeup pipe function to be called in between WakeMany()'s API call + sock->SetWrapFn(ToggleWakeupPipe); + // Add node NodeId id = GetNewNodeId(); uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize(); @@ -2388,7 +2391,8 @@ void CConnman::SocketHandler(CMasternodeSync& mn_sync) // select(2)). If none are ready, wait for a short while and return // empty sets. events_per_sock = GenerateWaitSockets(snap.Nodes()); - if ((is_lt && events_per_sock.empty()) || !Sock::IWaitMany(socketEventsMode, GetModeFileDescriptor(), timeout, events_per_sock)) { + if ((is_lt && events_per_sock.empty()) || !Sock::IWaitMany(socketEventsMode, GetModeFileDescriptor(), + ToggleWakeupPipe, timeout, events_per_sock)) { if (is_lt) { interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)); } @@ -3723,6 +3727,9 @@ bool CConnman::BindListenPort(const CService& addrBind, bilingual_str& strError, return false; } + // Set toggle wakeup pipe function to be called in between WakeMany()'s API call + sock->SetWrapFn(ToggleWakeupPipe); + if (m_edge_trig_events && !m_edge_trig_events->AddSocket(sock->Get())) { LogPrintf("Error: EdgeTriggeredEvents::AddSocket() failed\n"); return false; diff --git a/src/net.h b/src/net.h index b9ec00442052a..d7c124f67c51a 100644 --- a/src/net.h +++ b/src/net.h @@ -1813,15 +1813,13 @@ friend class CNode; return std::nullopt; } - template - void ToggleWakeupPipe(Callable&& func) - { + wrap_fn ToggleWakeupPipe = [&](std::function&& func) { if (m_wakeup_pipe) { m_wakeup_pipe->Toggle(func); } else { func(); } - } + }; Mutex cs_sendable_receivable_nodes; std::unordered_map mapReceivableNodes GUARDED_BY(cs_sendable_receivable_nodes); diff --git a/src/util/sock.cpp b/src/util/sock.cpp index ac8782db8b806..2475143dfdf60 100644 --- a/src/util/sock.cpp +++ b/src/util/sock.cpp @@ -165,7 +165,7 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occur /* We need to specify that we only want level-triggered modes used because we are expecting a direct correlation between the events reported and the one socket we are querying */ - if (!IWaitMany(m_event_mode, m_fd_mode, timeout, events_per_sock, /* lt_only = */ true)) { + if (!IWaitMany(m_event_mode, m_fd_mode, m_wrap_func, timeout, events_per_sock, /* lt_only = */ true)) { return false; } @@ -178,33 +178,33 @@ bool Sock::Wait(std::chrono::milliseconds timeout, Event requested, Event* occur bool Sock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const { - return IWaitMany(m_event_mode, m_fd_mode, timeout, events_per_sock); + return IWaitMany(m_event_mode, m_fd_mode, m_wrap_func, timeout, events_per_sock); } -bool Sock::IWaitMany(SocketEventsMode event_mode, std::optional fd_mode, std::chrono::milliseconds timeout, - EventsPerSock& events_per_sock, bool lt_only) +bool Sock::IWaitMany(SocketEventsMode event_mode, std::optional fd_mode, wrap_fn wrap_func, + std::chrono::milliseconds timeout, EventsPerSock& events_per_sock, bool lt_only) { switch (event_mode) { #ifdef USE_POLL case SocketEventsMode::Poll: - return WaitManyPoll(timeout, events_per_sock); + return WaitManyPoll(wrap_func, timeout, events_per_sock); #endif /* USE_POLL */ case SocketEventsMode::Select: - return WaitManySelect(timeout, events_per_sock); + return WaitManySelect(wrap_func, timeout, events_per_sock); #ifdef USE_EPOLL case SocketEventsMode::EPoll: /* If we explictly need to use a level triggered mode, skip below to fallback */ if (lt_only) break; assert(fd_mode); - return WaitManyEPoll(fd_mode.value(), timeout, events_per_sock); + return WaitManyEPoll(fd_mode.value(), wrap_func, timeout, events_per_sock); #endif /* USE_EPOLL */ #ifdef USE_KQUEUE case SocketEventsMode::KQueue: /* If we explictly need to use a level triggered mode, skip below to fallback */ if (lt_only) break; assert(fd_mode); - return WaitManyKQueue(fd_mode.value(), timeout, events_per_sock); + return WaitManyKQueue(fd_mode.value(), wrap_func, timeout, events_per_sock); #endif /* USE_KQUEUE */ default: assert(false); @@ -212,18 +212,22 @@ bool Sock::IWaitMany(SocketEventsMode event_mode, std::optional fd_mode, st /* We should only be here if we need a level-triggered mode but the instance is edge-triggered instead */ assert(lt_only); #ifdef USE_POLL - return WaitManyPoll(timeout, events_per_sock); + return WaitManyPoll(wrap_func, timeout, events_per_sock); #else - return WaitManySelect(timeout, events_per_sock); + return WaitManySelect(wrap_func, timeout, events_per_sock); #endif /* USE_POLL */ } #ifdef USE_EPOLL -bool Sock::WaitManyEPoll(int fd_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) +bool Sock::WaitManyEPoll(int fd_mode, wrap_fn wrap_func, + std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) { std::array events; - int ret = epoll_wait(fd_mode, events.data(), events.size(), count_milliseconds(timeout)); + int ret{SOCKET_ERROR}; + wrap_func([&](){ + ret = epoll_wait(fd_mode, events.data(), events.size(), count_milliseconds(timeout)); + }); if (ret == SOCKET_ERROR) { return false; } @@ -253,12 +257,16 @@ bool Sock::WaitManyEPoll(int fd_mode, std::chrono::milliseconds timeout, EventsP #endif /* USE_EPOLL */ #ifdef USE_KQUEUE -bool Sock::WaitManyKQueue(int fd_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) +bool Sock::WaitManyKQueue(int fd_mode, wrap_fn wrap_func, + std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) { std::array events; struct timespec ts = MillisToTimespec(timeout); - int ret = kevent(fd_mode, nullptr, 0, events.data(), events.size(), &ts); + int ret{SOCKET_ERROR}; + wrap_func([&](){ + ret = kevent(fd_mode, nullptr, 0, events.data(), events.size(), &ts); + }); if (ret == SOCKET_ERROR) { return false; } @@ -288,7 +296,7 @@ bool Sock::WaitManyKQueue(int fd_mode, std::chrono::milliseconds timeout, Events #endif /* USE_KQUEUE */ #ifdef USE_POLL -bool Sock::WaitManyPoll(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) +bool Sock::WaitManyPoll(wrap_fn wrap_func, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) { std::vector pfds; for (const auto& [socket, events] : events_per_sock) { @@ -303,7 +311,11 @@ bool Sock::WaitManyPoll(std::chrono::milliseconds timeout, EventsPerSock& events } } - if (poll(pfds.data(), pfds.size(), count_milliseconds(timeout)) == SOCKET_ERROR) { + int ret{SOCKET_ERROR}; + wrap_func([&](){ + ret = poll(pfds.data(), pfds.size(), count_milliseconds(timeout)); + }); + if (ret == SOCKET_ERROR) { return false; } @@ -328,7 +340,7 @@ bool Sock::WaitManyPoll(std::chrono::milliseconds timeout, EventsPerSock& events } #endif /* USE_POLL */ -bool Sock::WaitManySelect(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) +bool Sock::WaitManySelect(wrap_fn wrap_func, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) { fd_set recv; fd_set send; @@ -355,7 +367,11 @@ bool Sock::WaitManySelect(std::chrono::milliseconds timeout, EventsPerSock& even timeval tv = MillisToTimeval(timeout); - if (select(socket_max + 1, &recv, &send, &err, &tv) == SOCKET_ERROR) { + int ret{SOCKET_ERROR}; + wrap_func([&](){ + ret = select(socket_max + 1, &recv, &send, &err, &tv); + }); + if (ret == SOCKET_ERROR) { return false; } @@ -376,6 +392,11 @@ bool Sock::WaitManySelect(std::chrono::milliseconds timeout, EventsPerSock& even return true; } +void Sock::SetWrapFn(wrap_fn wrap_func) +{ + m_wrap_func = wrap_func; +} + void Sock::SendComplete(const std::string& data, std::chrono::milliseconds timeout, CThreadInterrupt& interrupt) const diff --git a/src/util/sock.h b/src/util/sock.h index c1123538d2216..f5d3be77d87d5 100644 --- a/src/util/sock.h +++ b/src/util/sock.h @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -70,6 +71,8 @@ static SocketEventsMode SEMFromString(const std::string str) else { return SocketEventsMode::Unknown; } } +using wrap_fn = std::function&&)>; + /** * RAII helper class that manages a socket. Mimics `std::unique_ptr`, but instead of a pointer it * contains a socket and closes it automatically when it goes out of scope. @@ -273,18 +276,25 @@ class Sock * This doesn't apply to Sock::Wait(), as it populates an EventsPerSock map with its own raw * socket before passing it to WaitMany. */ - static bool IWaitMany(SocketEventsMode event_mode, std::optional fd_mode, std::chrono::milliseconds timeout, - EventsPerSock& events_per_sock, bool lt_only = false); + static bool IWaitMany(SocketEventsMode event_mode, std::optional fd_mode, wrap_fn wrap_func, + std::chrono::milliseconds timeout, EventsPerSock& events_per_sock, bool lt_only = false); #ifdef USE_EPOLL - static bool WaitManyEPoll(int fd_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); + static bool WaitManyEPoll(int fd_mode, wrap_fn wrap_func, + std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); #endif /* USE_EPOLL */ #ifdef USE_KQUEUE - static bool WaitManyKQueue(int fd_mode, std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); + static bool WaitManyKQueue(int fd_mode, wrap_fn wrap_func, + std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); #endif /* USE_KQUEUE */ #ifdef USE_POLL - static bool WaitManyPoll(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); + static bool WaitManyPoll(wrap_fn wrap_func, + std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); #endif /* USE_POLL */ - static bool WaitManySelect(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); + static bool WaitManySelect(wrap_fn wrap_func, + std::chrono::milliseconds timeout, EventsPerSock& events_per_sock); + + /* Set function wrapped around WaitMany()'s API call */ + void SetWrapFn(wrap_fn wrap_func); /* Higher level, convenience, methods. These may throw. */ @@ -333,6 +343,8 @@ class Sock SocketEventsMode m_event_mode{SocketEventsMode::Unknown}; /* Optional containing file descriptor for applicable event modes */ std::optional m_fd_mode{std::nullopt}; + /* Function that wraps itself around WakeMany()'s API call */ + wrap_fn m_wrap_func{[](std::function&& func){func();}}; }; /** Return readable error string for a network error code */ From 76290a0ce2fc24aea1fe763eec30c18a777f4209 Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Thu, 9 May 2024 16:14:38 +0000 Subject: [PATCH 7/8] trivial: cleanup unused `CConnman::SocketEvents` variants --- src/net.cpp | 81 ----------------------------------------------------- src/net.h | 13 --------- 2 files changed, 94 deletions(-) diff --git a/src/net.cpp b/src/net.cpp index 9e1b23ff9852f..80ea1f90bc710 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -54,14 +54,6 @@ #include #endif -#ifdef USE_EPOLL -#include -#endif - -#ifdef USE_KQUEUE -#include -#endif - #include #include #include @@ -2280,79 +2272,6 @@ Sock::EventsPerSock CConnman::GenerateWaitSockets(Span nodes) return events_per_sock; } -#ifdef USE_KQUEUE -void CConnman::SocketEventsKqueue(std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll) -{ - std::array events; - struct timespec timeout = MillisToTimespec(only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS); - - int n{-1}; - ToggleWakeupPipe([&](){ - n = kevent(Assert(m_edge_trig_events)->GetFileDescriptor(), nullptr, 0, events.data(), events.size(), &timeout); - }); - if (n == -1) { - LogPrintf("kevent wait error\n"); - return; - } - - for (int i = 0; i < n; i++) { - auto& event = events[i]; - if ((event.flags & EV_ERROR) || (event.flags & EV_EOF)) { - error_set.insert((SOCKET)event.ident); - continue; - } - - if (event.filter == EVFILT_READ) { - recv_set.insert((SOCKET)event.ident); - } - - if (event.filter == EVFILT_WRITE) { - send_set.insert((SOCKET)event.ident); - } - } -} -#endif - -#ifdef USE_EPOLL -void CConnman::SocketEventsEpoll(std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll) -{ - std::array events; - - int n{-1}; - ToggleWakeupPipe([&](){ - n = epoll_wait(Assert(m_edge_trig_events)->GetFileDescriptor(), events.data(), events.size(), - only_poll ? 0 : SELECT_TIMEOUT_MILLISECONDS); - }); - - if (n == -1) { - LogPrintf("epoll_wait error\n"); - return; - } - - for (int i = 0; i < n; i++) { - auto& e = events[i]; - if((e.events & EPOLLERR) || (e.events & EPOLLHUP)) { - error_set.insert((SOCKET)e.data.fd); - continue; - } - - if (e.events & EPOLLIN) { - recv_set.insert((SOCKET)e.data.fd); - } - - if (e.events & EPOLLOUT) { - send_set.insert((SOCKET)e.data.fd); - } - } -} -#endif - void CConnman::SocketHandler(CMasternodeSync& mn_sync) { AssertLockNotHeld(m_total_bytes_sent_mutex); diff --git a/src/net.h b/src/net.h index d7c124f67c51a..5cb0751e7703c 100644 --- a/src/net.h +++ b/src/net.h @@ -1579,19 +1579,6 @@ friend class CNode; */ Sock::EventsPerSock GenerateWaitSockets(Span nodes); -#ifdef USE_KQUEUE - void SocketEventsKqueue(std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll); -#endif -#ifdef USE_EPOLL - void SocketEventsEpoll(std::set& recv_set, - std::set& send_set, - std::set& error_set, - bool only_poll); -#endif - /** * Check connected and listening sockets for IO readiness and process them accordingly. */ From 4ecf5049030d5083feec53aae418c011d04799fc Mon Sep 17 00:00:00 2001 From: Kittywhiskers Van Gogh <63189531+kwvg@users.noreply.github.com> Date: Thu, 9 May 2024 16:18:39 +0000 Subject: [PATCH 8/8] refactor: move `DEFAULT_SOCKETEVENTS` to `util/sock.h` --- src/net.h | 10 ---------- src/util/sock.h | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/net.h b/src/net.h index 5cb0751e7703c..f427e66a57293 100644 --- a/src/net.h +++ b/src/net.h @@ -114,16 +114,6 @@ static const bool DEFAULT_FIXEDSEEDS = true; static const size_t DEFAULT_MAXRECEIVEBUFFER = 5 * 1000; static const size_t DEFAULT_MAXSENDBUFFER = 1 * 1000; -#if defined USE_KQUEUE -#define DEFAULT_SOCKETEVENTS "kqueue" -#elif defined USE_EPOLL -#define DEFAULT_SOCKETEVENTS "epoll" -#elif defined USE_POLL -#define DEFAULT_SOCKETEVENTS "poll" -#else -#define DEFAULT_SOCKETEVENTS "select" -#endif - typedef int64_t NodeId; struct AddedNodeInfo diff --git a/src/util/sock.h b/src/util/sock.h index f5d3be77d87d5..294663e4dd863 100644 --- a/src/util/sock.h +++ b/src/util/sock.h @@ -16,6 +16,16 @@ #include #include +#if defined(USE_EPOLL) +#define DEFAULT_SOCKETEVENTS "epoll" +#elif defined(USE_KQUEUE) +#define DEFAULT_SOCKETEVENTS "kqueue" +#elif defined(USE_POLL) +#define DEFAULT_SOCKETEVENTS "poll" +#else +#define DEFAULT_SOCKETEVENTS "select" +#endif + /** * Maximum time to wait for I/O readiness. * It will take up until this time to break off in case of an interruption.