diff --git a/src/net.cpp b/src/net.cpp index 3a1bb138ab..456b043ba0 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -1361,6 +1361,9 @@ bool CConnman::GenerateSelectSet(std::set &recv_set, std::set &s // write buffer in this case before receiving more. This avoids // needlessly queueing received data, if the remote peer is not themselves // receiving data. This means properly utilizing TCP flow control signalling. + // This logic can put both nodes in deadlock if they are both "not receiving", + // so there is a special case where we only stop receiving new messages, but + // keep processing the in-progress ones. // * Otherwise, if there is space left in the receive buffer, select() for // receiving data. // * Hand off all complete messages to the processor, to be handled without @@ -1380,7 +1383,9 @@ bool CConnman::GenerateSelectSet(std::set &recv_set, std::set &s error_set.insert(pnode->hSocket); if (select_send) { send_set.insert(pnode->hSocket); - continue; + // Only stop receiving new messages, but keep processing incomplete ones + if (!pnode->m_deserializer->IsEmpty()) + continue; } if (select_recv) { recv_set.insert(pnode->hSocket); diff --git a/src/net.h b/src/net.h index 1b6a7e0928..ba8fd6a5f6 100644 --- a/src/net.h +++ b/src/net.h @@ -302,6 +302,8 @@ class CNetMessage { */ class TransportDeserializer { public: + // returns true if the current deserialization is empty + virtual bool IsEmpty() const = 0; // returns true if the current deserialization is complete virtual bool Complete() const = 0; // set the serialization context version @@ -352,6 +354,10 @@ class V1TransportDeserializer final : public TransportDeserializer Reset(); } + bool IsEmpty() const override + { + return (nHdrPos == 0); + } bool Complete() const override { if (!in_data)