@@ -824,8 +824,13 @@ CNetMessage V1Transport::GetReceivedMessage(const std::chrono::microseconds time
824
824
return msg;
825
825
}
826
826
827
- void V1Transport::prepareForTransport (CSerializedNetMsg& msg, std::vector< unsigned char >& header) const
827
+ bool V1Transport::SetMessageToSend (CSerializedNetMsg& msg) noexcept
828
828
{
829
+ AssertLockNotHeld (m_send_mutex);
830
+ // Determine whether a new message can be set.
831
+ LOCK (m_send_mutex);
832
+ if (m_sending_header || m_bytes_sent < m_message_to_send.data .size ()) return false ;
833
+
829
834
// create dbl-sha256 checksum
830
835
uint256 hash = Hash (msg.data );
831
836
@@ -834,8 +839,50 @@ void V1Transport::prepareForTransport(CSerializedNetMsg& msg, std::vector<unsign
834
839
memcpy (hdr.pchChecksum , hash.begin (), CMessageHeader::CHECKSUM_SIZE);
835
840
836
841
// serialize header
837
- header.reserve (CMessageHeader::HEADER_SIZE);
838
- CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, header, 0 , hdr};
842
+ m_header_to_send.clear ();
843
+ CVectorWriter{SER_NETWORK, INIT_PROTO_VERSION, m_header_to_send, 0 , hdr};
844
+
845
+ // update state
846
+ m_message_to_send = std::move (msg);
847
+ m_sending_header = true ;
848
+ m_bytes_sent = 0 ;
849
+ return true ;
850
+ }
851
+
852
+ Transport::BytesToSend V1Transport::GetBytesToSend () const noexcept
853
+ {
854
+ AssertLockNotHeld (m_send_mutex);
855
+ LOCK (m_send_mutex);
856
+ if (m_sending_header) {
857
+ return {Span{m_header_to_send}.subspan (m_bytes_sent),
858
+ // We have more to send after the header if the message has payload.
859
+ !m_message_to_send.data .empty (),
860
+ m_message_to_send.m_type
861
+ };
862
+ } else {
863
+ return {Span{m_message_to_send.data }.subspan (m_bytes_sent),
864
+ // We never have more to send after this message's payload.
865
+ false ,
866
+ m_message_to_send.m_type
867
+ };
868
+ }
869
+ }
870
+
871
+ void V1Transport::MarkBytesSent (size_t bytes_sent) noexcept
872
+ {
873
+ AssertLockNotHeld (m_send_mutex);
874
+ LOCK (m_send_mutex);
875
+ m_bytes_sent += bytes_sent;
876
+ if (m_sending_header && m_bytes_sent == m_header_to_send.size ()) {
877
+ // We're done sending a message's header. Switch to sending its data bytes.
878
+ m_sending_header = false ;
879
+ m_bytes_sent = 0 ;
880
+ } else if (!m_sending_header && m_bytes_sent == m_message_to_send.data .size ()) {
881
+ // We're done sending a message's data. Wipe the data vector to reduce memory consumption.
882
+ m_message_to_send.data .clear ();
883
+ m_message_to_send.data .shrink_to_fit ();
884
+ m_bytes_sent = 0 ;
885
+ }
839
886
}
840
887
841
888
std::pair<size_t , bool > CConnman::SocketSendData (CNode& node) const
@@ -2910,27 +2957,40 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
2910
2957
msg.data .data ()
2911
2958
);
2912
2959
2913
- // make sure we use the appropriate network transport format
2914
- std::vector<unsigned char > serializedHeader;
2915
- pnode->m_transport ->prepareForTransport (msg, serializedHeader);
2916
- size_t nTotalSize = nMessageSize + serializedHeader.size ();
2917
-
2918
2960
size_t nBytesSent = 0 ;
2919
2961
{
2920
2962
LOCK (pnode->cs_vSend );
2921
- bool optimisticSend (pnode->vSendMsg .empty ());
2922
-
2923
- // log total amount of bytes per message type
2924
- pnode->AccountForSentBytes (msg.m_type , nTotalSize);
2925
- pnode->nSendSize += nTotalSize;
2926
-
2927
- if (pnode->nSendSize > nSendBufferMaxSize) pnode->fPauseSend = true ;
2928
- pnode->vSendMsg .push_back (std::move (serializedHeader));
2929
- if (nMessageSize) pnode->vSendMsg .push_back (std::move (msg.data ));
2930
-
2931
- // If write queue empty, attempt "optimistic write"
2932
- bool data_left;
2933
- if (optimisticSend) std::tie (nBytesSent, data_left) = SocketSendData (*pnode);
2963
+ const bool queue_was_empty{pnode->vSendMsg .empty ()};
2964
+
2965
+ // Give the message to the transport, and add all bytes it wants us to send out as byte
2966
+ // vectors to vSendMsg. This is temporary code that exists to support the new transport
2967
+ // sending interface using the old way of queueing data. In a future commit vSendMsg will
2968
+ // be replaced with a queue of CSerializedNetMsg objects to be sent instead, and this code
2969
+ // will disappear.
2970
+ bool queued = pnode->m_transport ->SetMessageToSend (msg);
2971
+ assert (queued);
2972
+ // In the current transport (V1Transport), GetBytesToSend first returns a header to send,
2973
+ // and then the payload data (if any), necessitating a loop.
2974
+ while (true ) {
2975
+ const auto & [bytes, _more, msg_type] = pnode->m_transport ->GetBytesToSend ();
2976
+ if (bytes.empty ()) break ;
2977
+ // Update statistics per message type.
2978
+ pnode->AccountForSentBytes (msg_type, bytes.size ());
2979
+ // Update number of bytes in the send buffer.
2980
+ pnode->nSendSize += bytes.size ();
2981
+ if (pnode->nSendSize > nSendBufferMaxSize) pnode->fPauseSend = true ;
2982
+ pnode->vSendMsg .push_back ({bytes.begin (), bytes.end ()});
2983
+ // Notify transport that bytes have been processed (they're not actually sent yet,
2984
+ // but pushed onto the vSendMsg queue of bytes to send).
2985
+ pnode->m_transport ->MarkBytesSent (bytes.size ());
2986
+ }
2987
+
2988
+ // If the write queue was empty before and isn't now, attempt "optimistic write":
2989
+ // because the poll/select loop may pause for SELECT_TIMEOUT_MILLISECONDS before actually
2990
+ // doing a send, try sending from the calling thread if the queue was empty before.
2991
+ if (queue_was_empty && !pnode->vSendMsg .empty ()) {
2992
+ std::tie (nBytesSent, std::ignore) = SocketSendData (*pnode);
2993
+ }
2934
2994
}
2935
2995
if (nBytesSent) RecordBytesSent (nBytesSent);
2936
2996
}
0 commit comments