@@ -827,7 +827,7 @@ void V1TransportSerializer::prepareForTransport(CSerializedNetMsg& msg, std::vec
827827 CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, header, 0 , hdr};
828828}
829829
830- size_t CConnman::SocketSendData (CNode& node) const
830+ std::pair< size_t , bool > CConnman::SocketSendData (CNode& node) const
831831{
832832 auto it = node.vSendMsg .begin ();
833833 size_t nSentSize = 0 ;
@@ -882,7 +882,7 @@ size_t CConnman::SocketSendData(CNode& node) const
882882 assert (node.nSendSize == 0 );
883883 }
884884 node.vSendMsg .erase (node.vSendMsg .begin (), it);
885- return nSentSize;
885+ return { nSentSize, !node. vSendMsg . empty ()} ;
886886}
887887
888888/* * Try to find a connection to evict when the node is full.
@@ -1217,37 +1217,15 @@ Sock::EventsPerSock CConnman::GenerateWaitSockets(Span<CNode* const> nodes)
12171217 }
12181218
12191219 for (CNode* pnode : nodes) {
1220- // Implement the following logic:
1221- // * If there is data to send, select() for sending data. As this only
1222- // happens when optimistic write failed, we choose to first drain the
1223- // write buffer in this case before receiving more. This avoids
1224- // needlessly queueing received data, if the remote peer is not themselves
1225- // receiving data. This means properly utilizing TCP flow control signalling.
1226- // * Otherwise, if there is space left in the receive buffer, select() for
1227- // receiving data.
1228- // * Hand off all complete messages to the processor, to be handled without
1229- // blocking here.
1230-
12311220 bool select_recv = !pnode->fPauseRecv ;
1232- bool select_send;
1233- {
1234- LOCK (pnode->cs_vSend );
1235- select_send = !pnode->vSendMsg .empty ();
1236- }
1221+ bool select_send = WITH_LOCK (pnode->cs_vSend , return !pnode->vSendMsg .empty ());
1222+ if (!select_recv && !select_send) continue ;
12371223
12381224 LOCK (pnode->m_sock_mutex );
1239- if (!pnode->m_sock ) {
1240- continue ;
1225+ if (pnode->m_sock ) {
1226+ Sock::Event event = (select_send ? Sock::SEND : 0 ) | (select_recv ? Sock::RECV : 0 );
1227+ events_per_sock.emplace (pnode->m_sock , Sock::Events{event});
12411228 }
1242-
1243- Sock::Event requested{0 };
1244- if (select_send) {
1245- requested = Sock::SEND;
1246- } else if (select_recv) {
1247- requested = Sock::RECV;
1248- }
1249-
1250- events_per_sock.emplace (pnode->m_sock , Sock::Events{requested});
12511229 }
12521230
12531231 return events_per_sock;
@@ -1308,6 +1286,24 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
13081286 errorSet = it->second .occurred & Sock::ERR;
13091287 }
13101288 }
1289+
1290+ if (sendSet) {
1291+ // Send data
1292+ auto [bytes_sent, data_left] = WITH_LOCK (pnode->cs_vSend , return SocketSendData (*pnode));
1293+ if (bytes_sent) {
1294+ RecordBytesSent (bytes_sent);
1295+
1296+ // If both receiving and (non-optimistic) sending were possible, we first attempt
1297+ // sending. If that succeeds, but does not fully drain the send queue, do not
1298+ // attempt to receive. This avoids needlessly queueing data if the remote peer
1299+ // is slow at receiving data, by means of TCP flow control. We only do this when
1300+ // sending actually succeeded to make sure progress is always made; otherwise a
1301+ // deadlock would be possible when both sides have data to send, but neither is
1302+ // receiving.
1303+ if (data_left) recvSet = false ;
1304+ }
1305+ }
1306+
13111307 if (recvSet || errorSet)
13121308 {
13131309 // typical socket buffer is 8K-64K
@@ -1354,12 +1350,6 @@ void CConnman::SocketHandlerConnected(const std::vector<CNode*>& nodes,
13541350 }
13551351 }
13561352
1357- if (sendSet) {
1358- // Send data
1359- size_t bytes_sent = WITH_LOCK (pnode->cs_vSend , return SocketSendData (*pnode));
1360- if (bytes_sent) RecordBytesSent (bytes_sent);
1361- }
1362-
13631353 if (InactivityCheck (*pnode)) pnode->fDisconnect = true ;
13641354 }
13651355}
@@ -2887,7 +2877,8 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
28872877 if (nMessageSize) pnode->vSendMsg .push_back (std::move (msg.data ));
28882878
28892879 // If write queue empty, attempt "optimistic write"
2890- if (optimisticSend) nBytesSent = SocketSendData (*pnode);
2880+ bool data_left;
2881+ if (optimisticSend) std::tie (nBytesSent, data_left) = SocketSendData (*pnode);
28912882 }
28922883 if (nBytesSent) RecordBytesSent (nBytesSent);
28932884}
0 commit comments