Skip to content

Commit 0ea92ca

Browse files
committed
Merge bitcoin/bitcoin#24356: refactor: replace CConnman::SocketEvents() with mockable Sock::WaitMany()
6e68ccb net: use Sock::WaitMany() instead of CConnman::SocketEvents() (Vasil Dimov) ae26346 net: introduce Sock::WaitMany() (Vasil Dimov) cc74459 net: also wait for exceptional events in Sock::Wait() (Vasil Dimov) Pull request description: _This is a piece of #21878, chopped off to ease review._ `Sock::Wait()` waits for IO events on one socket. Introduce a similar `virtual` method `WaitMany()` that waits simultaneously for IO events on more than one socket. Use `WaitMany()` instead of `CConnman::SocketEvents()` (and ditch the latter). Given that the former is a `virtual` method, it can be mocked by unit and fuzz tests. This will help to make bigger parts of `CConnman` testable (unit and fuzz). ACKs for top commit: laanwj: Code review ACK 6e68ccb jonatack: re-ACK 6e68ccb per `git range-diff e18fd47 6747729 6e68ccb`, and verified rebase to master and debug build Tree-SHA512: 917fb6ad880d64d3af1ebb301c06fbd01afd8ff043f49e4055a088ebed6affb7ffe1dcf59292d822f10de5f323b6d52d557cb081dd7434634995f9148efcf08f
2 parents 489b587 + 6e68ccb commit 0ea92ca

File tree

8 files changed

+199
-224
lines changed

8 files changed

+199
-224
lines changed

src/i2p.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,8 +150,8 @@ bool Session::Accept(Connection& conn)
150150
throw std::runtime_error("wait on socket failed");
151151
}
152152

153-
if ((occurred & Sock::RECV) == 0) {
154-
// Timeout, no incoming connections within MAX_WAIT_FOR_IO.
153+
if (occurred == 0) {
154+
// Timeout, no incoming connections or errors within MAX_WAIT_FOR_IO.
155155
continue;
156156
}
157157

src/net.cpp

Lines changed: 29 additions & 149 deletions
Original file line numberDiff line numberDiff line change
@@ -1404,13 +1404,12 @@ bool CConnman::InactivityCheck(const CNode& node) const
14041404
return false;
14051405
}
14061406

1407-
bool CConnman::GenerateSelectSet(const std::vector<CNode*>& nodes,
1408-
std::set<SOCKET>& recv_set,
1409-
std::set<SOCKET>& send_set,
1410-
std::set<SOCKET>& error_set)
1407+
Sock::EventsPerSock CConnman::GenerateWaitSockets(Span<CNode* const> nodes)
14111408
{
1409+
Sock::EventsPerSock events_per_sock;
1410+
14121411
for (const ListenSocket& hListenSocket : vhListenSocket) {
1413-
recv_set.insert(hListenSocket.sock->Get());
1412+
events_per_sock.emplace(hListenSocket.sock, Sock::Events{Sock::RECV});
14141413
}
14151414

14161415
for (CNode* pnode : nodes) {
@@ -1437,172 +1436,49 @@ bool CConnman::GenerateSelectSet(const std::vector<CNode*>& nodes,
14371436
continue;
14381437
}
14391438

1440-
error_set.insert(pnode->m_sock->Get());
1439+
Sock::Event requested{0};
14411440
if (select_send) {
1442-
send_set.insert(pnode->m_sock->Get());
1443-
continue;
1444-
}
1445-
if (select_recv) {
1446-
recv_set.insert(pnode->m_sock->Get());
1447-
}
1448-
}
1449-
1450-
return !recv_set.empty() || !send_set.empty() || !error_set.empty();
1451-
}
1452-
1453-
#ifdef USE_POLL
1454-
void CConnman::SocketEvents(const std::vector<CNode*>& nodes,
1455-
std::set<SOCKET>& recv_set,
1456-
std::set<SOCKET>& send_set,
1457-
std::set<SOCKET>& error_set)
1458-
{
1459-
std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
1460-
if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) {
1461-
interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS));
1462-
return;
1463-
}
1464-
1465-
std::unordered_map<SOCKET, struct pollfd> pollfds;
1466-
for (SOCKET socket_id : recv_select_set) {
1467-
pollfds[socket_id].fd = socket_id;
1468-
pollfds[socket_id].events |= POLLIN;
1469-
}
1470-
1471-
for (SOCKET socket_id : send_select_set) {
1472-
pollfds[socket_id].fd = socket_id;
1473-
pollfds[socket_id].events |= POLLOUT;
1474-
}
1475-
1476-
for (SOCKET socket_id : error_select_set) {
1477-
pollfds[socket_id].fd = socket_id;
1478-
// These flags are ignored, but we set them for clarity
1479-
pollfds[socket_id].events |= POLLERR|POLLHUP;
1480-
}
1481-
1482-
std::vector<struct pollfd> vpollfds;
1483-
vpollfds.reserve(pollfds.size());
1484-
for (auto it : pollfds) {
1485-
vpollfds.push_back(std::move(it.second));
1486-
}
1487-
1488-
if (poll(vpollfds.data(), vpollfds.size(), SELECT_TIMEOUT_MILLISECONDS) < 0) return;
1489-
1490-
if (interruptNet) return;
1491-
1492-
for (struct pollfd pollfd_entry : vpollfds) {
1493-
if (pollfd_entry.revents & POLLIN) recv_set.insert(pollfd_entry.fd);
1494-
if (pollfd_entry.revents & POLLOUT) send_set.insert(pollfd_entry.fd);
1495-
if (pollfd_entry.revents & (POLLERR|POLLHUP)) error_set.insert(pollfd_entry.fd);
1496-
}
1497-
}
1498-
#else
1499-
void CConnman::SocketEvents(const std::vector<CNode*>& nodes,
1500-
std::set<SOCKET>& recv_set,
1501-
std::set<SOCKET>& send_set,
1502-
std::set<SOCKET>& error_set)
1503-
{
1504-
std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
1505-
if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) {
1506-
interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS));
1507-
return;
1508-
}
1509-
1510-
//
1511-
// Find which sockets have data to receive
1512-
//
1513-
struct timeval timeout;
1514-
timeout.tv_sec = 0;
1515-
timeout.tv_usec = SELECT_TIMEOUT_MILLISECONDS * 1000; // frequency to poll pnode->vSend
1516-
1517-
fd_set fdsetRecv;
1518-
fd_set fdsetSend;
1519-
fd_set fdsetError;
1520-
FD_ZERO(&fdsetRecv);
1521-
FD_ZERO(&fdsetSend);
1522-
FD_ZERO(&fdsetError);
1523-
SOCKET hSocketMax = 0;
1524-
1525-
for (SOCKET hSocket : recv_select_set) {
1526-
FD_SET(hSocket, &fdsetRecv);
1527-
hSocketMax = std::max(hSocketMax, hSocket);
1528-
}
1529-
1530-
for (SOCKET hSocket : send_select_set) {
1531-
FD_SET(hSocket, &fdsetSend);
1532-
hSocketMax = std::max(hSocketMax, hSocket);
1533-
}
1534-
1535-
for (SOCKET hSocket : error_select_set) {
1536-
FD_SET(hSocket, &fdsetError);
1537-
hSocketMax = std::max(hSocketMax, hSocket);
1538-
}
1539-
1540-
int nSelect = select(hSocketMax + 1, &fdsetRecv, &fdsetSend, &fdsetError, &timeout);
1541-
1542-
if (interruptNet)
1543-
return;
1544-
1545-
if (nSelect == SOCKET_ERROR)
1546-
{
1547-
int nErr = WSAGetLastError();
1548-
LogPrintf("socket select error %s\n", NetworkErrorString(nErr));
1549-
for (unsigned int i = 0; i <= hSocketMax; i++)
1550-
FD_SET(i, &fdsetRecv);
1551-
FD_ZERO(&fdsetSend);
1552-
FD_ZERO(&fdsetError);
1553-
if (!interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS)))
1554-
return;
1555-
}
1556-
1557-
for (SOCKET hSocket : recv_select_set) {
1558-
if (FD_ISSET(hSocket, &fdsetRecv)) {
1559-
recv_set.insert(hSocket);
1441+
requested = Sock::SEND;
1442+
} else if (select_recv) {
1443+
requested = Sock::RECV;
15601444
}
1561-
}
15621445

1563-
for (SOCKET hSocket : send_select_set) {
1564-
if (FD_ISSET(hSocket, &fdsetSend)) {
1565-
send_set.insert(hSocket);
1566-
}
1446+
events_per_sock.emplace(pnode->m_sock, Sock::Events{requested});
15671447
}
15681448

1569-
for (SOCKET hSocket : error_select_set) {
1570-
if (FD_ISSET(hSocket, &fdsetError)) {
1571-
error_set.insert(hSocket);
1572-
}
1573-
}
1449+
return events_per_sock;
15741450
}
1575-
#endif
15761451

15771452
void CConnman::SocketHandler()
15781453
{
15791454
AssertLockNotHeld(m_total_bytes_sent_mutex);
15801455

1581-
std::set<SOCKET> recv_set;
1582-
std::set<SOCKET> send_set;
1583-
std::set<SOCKET> error_set;
1456+
Sock::EventsPerSock events_per_sock;
15841457

15851458
{
15861459
const NodesSnapshot snap{*this, /*shuffle=*/false};
15871460

1461+
const auto timeout = std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS);
1462+
15881463
// Check for the readiness of the already connected sockets and the
15891464
// listening sockets in one call ("readiness" as in poll(2) or
15901465
// select(2)). If none are ready, wait for a short while and return
15911466
// empty sets.
1592-
SocketEvents(snap.Nodes(), recv_set, send_set, error_set);
1467+
events_per_sock = GenerateWaitSockets(snap.Nodes());
1468+
if (events_per_sock.empty() || !events_per_sock.begin()->first->WaitMany(timeout, events_per_sock)) {
1469+
interruptNet.sleep_for(timeout);
1470+
}
15931471

15941472
// Service (send/receive) each of the already connected nodes.
1595-
SocketHandlerConnected(snap.Nodes(), recv_set, send_set, error_set);
1473+
SocketHandlerConnected(snap.Nodes(), events_per_sock);
15961474
}
15971475

15981476
// Accept new connections from listening sockets.
1599-
SocketHandlerListening(recv_set);
1477+
SocketHandlerListening(events_per_sock);
16001478
}
16011479

16021480
void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
1603-
const std::set<SOCKET>& recv_set,
1604-
const std::set<SOCKET>& send_set,
1605-
const std::set<SOCKET>& error_set)
1481+
const Sock::EventsPerSock& events_per_sock)
16061482
{
16071483
AssertLockNotHeld(m_total_bytes_sent_mutex);
16081484

@@ -1621,9 +1497,12 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
16211497
if (!pnode->m_sock) {
16221498
continue;
16231499
}
1624-
recvSet = recv_set.count(pnode->m_sock->Get()) > 0;
1625-
sendSet = send_set.count(pnode->m_sock->Get()) > 0;
1626-
errorSet = error_set.count(pnode->m_sock->Get()) > 0;
1500+
const auto it = events_per_sock.find(pnode->m_sock);
1501+
if (it != events_per_sock.end()) {
1502+
recvSet = it->second.occurred & Sock::RECV;
1503+
sendSet = it->second.occurred & Sock::SEND;
1504+
errorSet = it->second.occurred & Sock::ERR;
1505+
}
16271506
}
16281507
if (recvSet || errorSet)
16291508
{
@@ -1693,13 +1572,14 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
16931572
}
16941573
}
16951574

1696-
void CConnman::SocketHandlerListening(const std::set<SOCKET>& recv_set)
1575+
void CConnman::SocketHandlerListening(const Sock::EventsPerSock& events_per_sock)
16971576
{
16981577
for (const ListenSocket& listen_socket : vhListenSocket) {
16991578
if (interruptNet) {
17001579
return;
17011580
}
1702-
if (recv_set.count(listen_socket.sock->Get()) > 0) {
1581+
const auto it = events_per_sock.find(listen_socket.sock);
1582+
if (it != events_per_sock.end() && it->second.occurred & Sock::RECV) {
17031583
AcceptConnection(listen_socket);
17041584
}
17051585
}

src/net.h

Lines changed: 7 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -980,28 +980,9 @@ class CConnman
980980
/**
981981
* Generate a collection of sockets to check for IO readiness.
982982
* @param[in] nodes Select from these nodes' sockets.
983-
* @param[out] recv_set Sockets to check for read readiness.
984-
* @param[out] send_set Sockets to check for write readiness.
985-
* @param[out] error_set Sockets to check for errors.
986-
* @return true if at least one socket is to be checked (the returned set is not empty)
983+
* @return sockets to check for readiness
987984
*/
988-
bool GenerateSelectSet(const std::vector<CNode*>& nodes,
989-
std::set<SOCKET>& recv_set,
990-
std::set<SOCKET>& send_set,
991-
std::set<SOCKET>& error_set);
992-
993-
/**
994-
* Check which sockets are ready for IO.
995-
* @param[in] nodes Select from these nodes' sockets.
996-
* @param[out] recv_set Sockets which are ready for read.
997-
* @param[out] send_set Sockets which are ready for write.
998-
* @param[out] error_set Sockets which have errors.
999-
* This calls `GenerateSelectSet()` to gather a list of sockets to check.
1000-
*/
1001-
void SocketEvents(const std::vector<CNode*>& nodes,
1002-
std::set<SOCKET>& recv_set,
1003-
std::set<SOCKET>& send_set,
1004-
std::set<SOCKET>& error_set);
985+
Sock::EventsPerSock GenerateWaitSockets(Span<CNode* const> nodes);
1005986

1006987
/**
1007988
* Check connected and listening sockets for IO readiness and process them accordingly.
@@ -1010,23 +991,18 @@ class CConnman
1010991

1011992
/**
1012993
* Do the read/write for connected sockets that are ready for IO.
1013-
* @param[in] nodes Nodes to process. The socket of each node is checked against
1014-
* `recv_set`, `send_set` and `error_set`.
1015-
* @param[in] recv_set Sockets that are ready for read.
1016-
* @param[in] send_set Sockets that are ready for send.
1017-
* @param[in] error_set Sockets that have an exceptional condition (error).
994+
* @param[in] nodes Nodes to process. The socket of each node is checked against `what`.
995+
* @param[in] events_per_sock Sockets that are ready for IO.
1018996
*/
1019997
void SocketHandlerConnected(const std::vector<CNode*>& nodes,
1020-
const std::set<SOCKET>& recv_set,
1021-
const std::set<SOCKET>& send_set,
1022-
const std::set<SOCKET>& error_set)
998+
const Sock::EventsPerSock& events_per_sock)
1023999
EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc);
10241000

10251001
/**
10261002
* Accept incoming connections, one from each read-ready listening socket.
1027-
* @param[in] recv_set Sockets that are ready for read.
1003+
* @param[in] events_per_sock Sockets that are ready for IO.
10281004
*/
1029-
void SocketHandlerListening(const std::set<SOCKET>& recv_set);
1005+
void SocketHandlerListening(const Sock::EventsPerSock& events_per_sock);
10301006

10311007
void ThreadSocketHandler() EXCLUSIVE_LOCKS_REQUIRED(!m_total_bytes_sent_mutex, !mutexMsgProc);
10321008
void ThreadDNSAddressSeed() EXCLUSIVE_LOCKS_REQUIRED(!m_addr_fetches_mutex, !m_nodes_mutex);

src/test/fuzz/util.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,15 @@ bool FuzzedSock::Wait(std::chrono::milliseconds timeout, Event requested, Event*
223223
return true;
224224
}
225225

226+
bool FuzzedSock::WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const
227+
{
228+
for (auto& [sock, events] : events_per_sock) {
229+
(void)sock;
230+
events.occurred = m_fuzzed_data_provider.ConsumeBool() ? events.requested : 0;
231+
}
232+
return true;
233+
}
234+
226235
bool FuzzedSock::IsConnected(std::string& errmsg) const
227236
{
228237
if (m_fuzzed_data_provider.ConsumeBool()) {

src/test/fuzz/util.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ class FuzzedSock : public Sock
7171

7272
bool Wait(std::chrono::milliseconds timeout, Event requested, Event* occurred = nullptr) const override;
7373

74+
bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override;
75+
7476
bool IsConnected(std::string& errmsg) const override;
7577
};
7678

src/test/util/net.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,15 @@ class StaticContentsSock : public Sock
162162
return true;
163163
}
164164

165+
bool WaitMany(std::chrono::milliseconds timeout, EventsPerSock& events_per_sock) const override
166+
{
167+
for (auto& [sock, events] : events_per_sock) {
168+
(void)sock;
169+
events.occurred = events.requested;
170+
}
171+
return true;
172+
}
173+
165174
private:
166175
const std::string m_contents;
167176
mutable size_t m_consumed;

0 commit comments

Comments
 (0)