Skip to content

Commit 64059b7

Browse files
committed
Merge bitcoin/bitcoin#21943: Dedup and RAII-fy the creation of a copy of CConnman::vNodes
f52b6b2 net: split CConnman::SocketHandler() (Vasil Dimov) c7eb19e style: remove unnecessary braces (Vasil Dimov) 664ac22 net: keep reference to each node during socket wait (Vasil Dimov) 75e8bf5 net: dedup and RAII-fy the creation of a copy of CConnman::vNodes (Vasil Dimov) Pull request description: _This is a piece of bitcoin/bitcoin#21878, chopped off to ease review._ The following pattern was duplicated in CConnman: ```cpp lock create a copy of vNodes, add a reference to each one unlock ... use the copy ... lock release each node from the copy unlock ``` Put that code in a RAII helper that reduces it to: ```cpp create snapshot "snap" ... use the copy ... // release happens when "snap" goes out of scope ACKs for top commit: jonatack: ACK f52b6b2 changes since last review are reordered commits, removing an unneeded local variable, and code formatting and documentation improvements LarryRuane: code review ACK f52b6b2 promag: Code review ACK f52b6b2, only format changes and comment tweaks since last review. Tree-SHA512: 5ead7b4c641ebe5b215e7baeb7bc0cdab2a588b2871d9a343a1d518535c55c0353d4e46de663f41513cdcc79262938ccea3232f6d5166570fc2230286c985f68
2 parents 9394964 + f52b6b2 commit 64059b7

File tree

2 files changed

+182
-101
lines changed

2 files changed

+182
-101
lines changed

src/net.cpp

Lines changed: 95 additions & 99 deletions
Original file line numberDiff line numberDiff line change
@@ -1353,57 +1353,59 @@ bool CConnman::InactivityCheck(const CNode& node) const
13531353
return false;
13541354
}
13551355

1356-
bool CConnman::GenerateSelectSet(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set)
1356+
bool CConnman::GenerateSelectSet(const std::vector<CNode*>& nodes,
1357+
std::set<SOCKET>& recv_set,
1358+
std::set<SOCKET>& send_set,
1359+
std::set<SOCKET>& error_set)
13571360
{
13581361
for (const ListenSocket& hListenSocket : vhListenSocket) {
13591362
recv_set.insert(hListenSocket.socket);
13601363
}
13611364

1362-
{
1363-
LOCK(cs_vNodes);
1364-
for (CNode* pnode : vNodes)
1365+
for (CNode* pnode : nodes) {
1366+
// Implement the following logic:
1367+
// * If there is data to send, select() for sending data. As this only
1368+
// happens when optimistic write failed, we choose to first drain the
1369+
// write buffer in this case before receiving more. This avoids
1370+
// needlessly queueing received data, if the remote peer is not themselves
1371+
// receiving data. This means properly utilizing TCP flow control signalling.
1372+
// * Otherwise, if there is space left in the receive buffer, select() for
1373+
// receiving data.
1374+
// * Hand off all complete messages to the processor, to be handled without
1375+
// blocking here.
1376+
1377+
bool select_recv = !pnode->fPauseRecv;
1378+
bool select_send;
13651379
{
1366-
// Implement the following logic:
1367-
// * If there is data to send, select() for sending data. As this only
1368-
// happens when optimistic write failed, we choose to first drain the
1369-
// write buffer in this case before receiving more. This avoids
1370-
// needlessly queueing received data, if the remote peer is not themselves
1371-
// receiving data. This means properly utilizing TCP flow control signalling.
1372-
// * Otherwise, if there is space left in the receive buffer, select() for
1373-
// receiving data.
1374-
// * Hand off all complete messages to the processor, to be handled without
1375-
// blocking here.
1376-
1377-
bool select_recv = !pnode->fPauseRecv;
1378-
bool select_send;
1379-
{
1380-
LOCK(pnode->cs_vSend);
1381-
select_send = !pnode->vSendMsg.empty();
1382-
}
1380+
LOCK(pnode->cs_vSend);
1381+
select_send = !pnode->vSendMsg.empty();
1382+
}
13831383

1384-
LOCK(pnode->cs_hSocket);
1385-
if (pnode->hSocket == INVALID_SOCKET)
1386-
continue;
1384+
LOCK(pnode->cs_hSocket);
1385+
if (pnode->hSocket == INVALID_SOCKET)
1386+
continue;
13871387

1388-
error_set.insert(pnode->hSocket);
1389-
if (select_send) {
1390-
send_set.insert(pnode->hSocket);
1391-
continue;
1392-
}
1393-
if (select_recv) {
1394-
recv_set.insert(pnode->hSocket);
1395-
}
1388+
error_set.insert(pnode->hSocket);
1389+
if (select_send) {
1390+
send_set.insert(pnode->hSocket);
1391+
continue;
1392+
}
1393+
if (select_recv) {
1394+
recv_set.insert(pnode->hSocket);
13961395
}
13971396
}
13981397

13991398
return !recv_set.empty() || !send_set.empty() || !error_set.empty();
14001399
}
14011400

14021401
#ifdef USE_POLL
1403-
void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set)
1402+
void CConnman::SocketEvents(const std::vector<CNode*>& nodes,
1403+
std::set<SOCKET>& recv_set,
1404+
std::set<SOCKET>& send_set,
1405+
std::set<SOCKET>& error_set)
14041406
{
14051407
std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
1406-
if (!GenerateSelectSet(recv_select_set, send_select_set, error_select_set)) {
1408+
if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) {
14071409
interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS));
14081410
return;
14091411
}
@@ -1442,10 +1444,13 @@ void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_s
14421444
}
14431445
}
14441446
#else
1445-
void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set)
1447+
void CConnman::SocketEvents(const std::vector<CNode*>& nodes,
1448+
std::set<SOCKET>& recv_set,
1449+
std::set<SOCKET>& send_set,
1450+
std::set<SOCKET>& error_set)
14461451
{
14471452
std::set<SOCKET> recv_select_set, send_select_set, error_select_set;
1448-
if (!GenerateSelectSet(recv_select_set, send_select_set, error_select_set)) {
1453+
if (!GenerateSelectSet(nodes, recv_select_set, send_select_set, error_select_set)) {
14491454
interruptNet.sleep_for(std::chrono::milliseconds(SELECT_TIMEOUT_MILLISECONDS));
14501455
return;
14511456
}
@@ -1519,34 +1524,33 @@ void CConnman::SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_s
15191524

15201525
void CConnman::SocketHandler()
15211526
{
1522-
std::set<SOCKET> recv_set, send_set, error_set;
1523-
SocketEvents(recv_set, send_set, error_set);
1524-
1525-
if (interruptNet) return;
1527+
std::set<SOCKET> recv_set;
1528+
std::set<SOCKET> send_set;
1529+
std::set<SOCKET> error_set;
15261530

1527-
//
1528-
// Accept new connections
1529-
//
1530-
for (const ListenSocket& hListenSocket : vhListenSocket)
15311531
{
1532-
if (hListenSocket.socket != INVALID_SOCKET && recv_set.count(hListenSocket.socket) > 0)
1533-
{
1534-
AcceptConnection(hListenSocket);
1535-
}
1536-
}
1532+
const NodesSnapshot snap{*this, /*shuffle=*/false};
15371533

1538-
//
1539-
// Service each socket
1540-
//
1541-
std::vector<CNode*> vNodesCopy;
1542-
{
1543-
LOCK(cs_vNodes);
1544-
vNodesCopy = vNodes;
1545-
for (CNode* pnode : vNodesCopy)
1546-
pnode->AddRef();
1534+
// Check for the readiness of the already connected sockets and the
1535+
// listening sockets in one call ("readiness" as in poll(2) or
1536+
// select(2)). If none are ready, wait for a short while and return
1537+
// empty sets.
1538+
SocketEvents(snap.Nodes(), recv_set, send_set, error_set);
1539+
1540+
// Service (send/receive) each of the already connected nodes.
1541+
SocketHandlerConnected(snap.Nodes(), recv_set, send_set, error_set);
15471542
}
1548-
for (CNode* pnode : vNodesCopy)
1549-
{
1543+
1544+
// Accept new connections from listening sockets.
1545+
SocketHandlerListening(recv_set);
1546+
}
1547+
1548+
void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
1549+
const std::set<SOCKET>& recv_set,
1550+
const std::set<SOCKET>& send_set,
1551+
const std::set<SOCKET>& error_set)
1552+
{
1553+
for (CNode* pnode : nodes) {
15501554
if (interruptNet)
15511555
return;
15521556

@@ -1628,10 +1632,17 @@ void CConnman::SocketHandler()
16281632

16291633
if (InactivityCheck(*pnode)) pnode->fDisconnect = true;
16301634
}
1631-
{
1632-
LOCK(cs_vNodes);
1633-
for (CNode* pnode : vNodesCopy)
1634-
pnode->Release();
1635+
}
1636+
1637+
void CConnman::SocketHandlerListening(const std::set<SOCKET>& recv_set)
1638+
{
1639+
for (const ListenSocket& listen_socket : vhListenSocket) {
1640+
if (interruptNet) {
1641+
return;
1642+
}
1643+
if (listen_socket.socket != INVALID_SOCKET && recv_set.count(listen_socket.socket) > 0) {
1644+
AcceptConnection(listen_socket);
1645+
}
16351646
}
16361647
}
16371648

@@ -2246,49 +2257,34 @@ void CConnman::OpenNetworkConnection(const CAddress& addrConnect, bool fCountFai
22462257
void CConnman::ThreadMessageHandler()
22472258
{
22482259
SetSyscallSandboxPolicy(SyscallSandboxPolicy::MESSAGE_HANDLER);
2249-
FastRandomContext rng;
22502260
while (!flagInterruptMsgProc)
22512261
{
2252-
std::vector<CNode*> vNodesCopy;
2253-
{
2254-
LOCK(cs_vNodes);
2255-
vNodesCopy = vNodes;
2256-
for (CNode* pnode : vNodesCopy) {
2257-
pnode->AddRef();
2258-
}
2259-
}
2260-
22612262
bool fMoreWork = false;
22622263

2263-
// Randomize the order in which we process messages from/to our peers.
2264-
// This prevents attacks in which an attacker exploits having multiple
2265-
// consecutive connections in the vNodes list.
2266-
Shuffle(vNodesCopy.begin(), vNodesCopy.end(), rng);
2267-
2268-
for (CNode* pnode : vNodesCopy)
22692264
{
2270-
if (pnode->fDisconnect)
2271-
continue;
2265+
// Randomize the order in which we process messages from/to our peers.
2266+
// This prevents attacks in which an attacker exploits having multiple
2267+
// consecutive connections in the vNodes list.
2268+
const NodesSnapshot snap{*this, /*shuffle=*/true};
22722269

2273-
// Receive messages
2274-
bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc);
2275-
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
2276-
if (flagInterruptMsgProc)
2277-
return;
2278-
// Send messages
2279-
{
2280-
LOCK(pnode->cs_sendProcessing);
2281-
m_msgproc->SendMessages(pnode);
2282-
}
2270+
for (CNode* pnode : snap.Nodes()) {
2271+
if (pnode->fDisconnect)
2272+
continue;
22832273

2284-
if (flagInterruptMsgProc)
2285-
return;
2286-
}
2274+
// Receive messages
2275+
bool fMoreNodeWork = m_msgproc->ProcessMessages(pnode, flagInterruptMsgProc);
2276+
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
2277+
if (flagInterruptMsgProc)
2278+
return;
2279+
// Send messages
2280+
{
2281+
LOCK(pnode->cs_sendProcessing);
2282+
m_msgproc->SendMessages(pnode);
2283+
}
22872284

2288-
{
2289-
LOCK(cs_vNodes);
2290-
for (CNode* pnode : vNodesCopy)
2291-
pnode->Release();
2285+
if (flagInterruptMsgProc)
2286+
return;
2287+
}
22922288
}
22932289

22942290
WAIT_LOCK(mutexMsgProc, lock);

src/net.h

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -983,9 +983,57 @@ class CConnman
983983
void NotifyNumConnectionsChanged();
984984
/** Return true if the peer is inactive and should be disconnected. */
985985
bool InactivityCheck(const CNode& node) const;
986-
bool GenerateSelectSet(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set);
987-
void SocketEvents(std::set<SOCKET> &recv_set, std::set<SOCKET> &send_set, std::set<SOCKET> &error_set);
986+
987+
/**
988+
* Generate a collection of sockets to check for IO readiness.
989+
* @param[in] nodes Select from these nodes' sockets.
990+
* @param[out] recv_set Sockets to check for read readiness.
991+
* @param[out] send_set Sockets to check for write readiness.
992+
* @param[out] error_set Sockets to check for errors.
993+
* @return true if at least one socket is to be checked (the returned set is not empty)
994+
*/
995+
bool GenerateSelectSet(const std::vector<CNode*>& nodes,
996+
std::set<SOCKET>& recv_set,
997+
std::set<SOCKET>& send_set,
998+
std::set<SOCKET>& error_set);
999+
1000+
/**
1001+
* Check which sockets are ready for IO.
1002+
* @param[in] nodes Select from these nodes' sockets.
1003+
* @param[out] recv_set Sockets which are ready for read.
1004+
* @param[out] send_set Sockets which are ready for write.
1005+
* @param[out] error_set Sockets which have errors.
1006+
* This calls `GenerateSelectSet()` to gather a list of sockets to check.
1007+
*/
1008+
void SocketEvents(const std::vector<CNode*>& nodes,
1009+
std::set<SOCKET>& recv_set,
1010+
std::set<SOCKET>& send_set,
1011+
std::set<SOCKET>& error_set);
1012+
1013+
/**
1014+
* Check connected and listening sockets for IO readiness and process them accordingly.
1015+
*/
9881016
void SocketHandler();
1017+
1018+
/**
1019+
* Do the read/write for connected sockets that are ready for IO.
1020+
* @param[in] nodes Nodes to process. The socket of each node is checked against
1021+
* `recv_set`, `send_set` and `error_set`.
1022+
* @param[in] recv_set Sockets that are ready for read.
1023+
* @param[in] send_set Sockets that are ready for send.
1024+
* @param[in] error_set Sockets that have an exceptional condition (error).
1025+
*/
1026+
void SocketHandlerConnected(const std::vector<CNode*>& nodes,
1027+
const std::set<SOCKET>& recv_set,
1028+
const std::set<SOCKET>& send_set,
1029+
const std::set<SOCKET>& error_set);
1030+
1031+
/**
1032+
* Accept incoming connections, one from each read-ready listening socket.
1033+
* @param[in] recv_set Sockets that are ready for read.
1034+
*/
1035+
void SocketHandlerListening(const std::set<SOCKET>& recv_set);
1036+
9891037
void ThreadSocketHandler();
9901038
void ThreadDNSAddressSeed();
9911039

@@ -1177,6 +1225,43 @@ class CConnman
11771225
*/
11781226
std::vector<CService> m_onion_binds;
11791227

1228+
/**
1229+
* RAII helper to atomically create a copy of `vNodes` and add a reference
1230+
* to each of the nodes. The nodes are released when this object is destroyed.
1231+
*/
1232+
class NodesSnapshot
1233+
{
1234+
public:
1235+
explicit NodesSnapshot(const CConnman& connman, bool shuffle)
1236+
{
1237+
{
1238+
LOCK(connman.cs_vNodes);
1239+
m_nodes_copy = connman.vNodes;
1240+
for (auto& node : m_nodes_copy) {
1241+
node->AddRef();
1242+
}
1243+
}
1244+
if (shuffle) {
1245+
Shuffle(m_nodes_copy.begin(), m_nodes_copy.end(), FastRandomContext{});
1246+
}
1247+
}
1248+
1249+
~NodesSnapshot()
1250+
{
1251+
for (auto& node : m_nodes_copy) {
1252+
node->Release();
1253+
}
1254+
}
1255+
1256+
const std::vector<CNode*>& Nodes() const
1257+
{
1258+
return m_nodes_copy;
1259+
}
1260+
1261+
private:
1262+
std::vector<CNode*> m_nodes_copy;
1263+
};
1264+
11801265
friend struct CConnmanTest;
11811266
friend struct ConnmanTestMsg;
11821267
};

0 commit comments

Comments
 (0)