Skip to content

Commit 8b66bf7

Browse files
committed
Merge #9441: Net: Massive speedup. Net locks overhaul
e60360e net: remove cs_vRecvMsg (Cory Fields) 991955e net: add a flag to indicate when a node's send buffer is full (Cory Fields) c6e8a9b net: add a flag to indicate when a node's process queue is full (Cory Fields) 4d712e3 net: add a new message queue for the message processor (Cory Fields) c5a8b1b net: rework the way that the messagehandler sleeps (Cory Fields) c72cc88 net: remove useless comments (Cory Fields) ef7b5ec net: Add a simple function for waking the message handler (Cory Fields) f5c36d1 net: record bytes written before notifying the message processor (Cory Fields) 60befa3 net: handle message accounting in ReceiveMsgBytes (Cory Fields) 56212e2 net: set message deserialization version when it's actually time to deserialize (Cory Fields) 0e973d9 net: remove redundant max sendbuffer size check (Cory Fields) 6042587 net: wait until the node is destroyed to delete its recv buffer (Cory Fields) f6315e0 net: only disconnect if fDisconnect has been set (Cory Fields) 5b4a8ac net: make GetReceiveFloodSize public (Cory Fields) e5bcd9c net: make vRecvMsg a list so that we can use splice() (Cory Fields) 53ad9a1 net: fix typo causing the wrong receive buffer size (Cory Fields)
2 parents 02e5308 + e60360e commit 8b66bf7

File tree

4 files changed

+107
-125
lines changed

4 files changed

+107
-125
lines changed

src/net.cpp

Lines changed: 57 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -437,11 +437,6 @@ void CNode::CloseSocketDisconnect()
437437
LogPrint("net", "disconnecting peer=%d\n", id);
438438
CloseSocket(hSocket);
439439
}
440-
441-
// in case this fails, we'll empty the recv buffer when the CNode is deleted
442-
TRY_LOCK(cs_vRecvMsg, lockRecv);
443-
if (lockRecv)
444-
vRecvMsg.clear();
445440
}
446441

447442
void CConnman::ClearBanned()
@@ -650,16 +645,18 @@ void CNode::copyStats(CNodeStats &stats)
650645
}
651646
#undef X
652647

653-
// requires LOCK(cs_vRecvMsg)
654648
bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete)
655649
{
656650
complete = false;
651+
int64_t nTimeMicros = GetTimeMicros();
652+
nLastRecv = nTimeMicros / 1000000;
653+
nRecvBytes += nBytes;
657654
while (nBytes > 0) {
658655

659656
// get current incomplete message, or create a new one
660657
if (vRecvMsg.empty() ||
661658
vRecvMsg.back().complete())
662-
vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, nRecvVersion));
659+
vRecvMsg.push_back(CNetMessage(Params().MessageStart(), SER_NETWORK, INIT_PROTO_VERSION));
663660

664661
CNetMessage& msg = vRecvMsg.back();
665662

@@ -691,7 +688,7 @@ bool CNode::ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete
691688
assert(i != mapRecvBytesPerMsgCmd.end());
692689
i->second += msg.hdr.nMessageSize + CMessageHeader::HEADER_SIZE;
693690

694-
msg.nTime = GetTimeMicros();
691+
msg.nTime = nTimeMicros;
695692
complete = true;
696693
}
697694
}
@@ -764,7 +761,7 @@ const uint256& CNetMessage::GetMessageHash() const
764761

765762

766763
// requires LOCK(cs_vSend)
767-
size_t SocketSendData(CNode *pnode)
764+
size_t CConnman::SocketSendData(CNode *pnode)
768765
{
769766
auto it = pnode->vSendMsg.begin();
770767
size_t nSentSize = 0;
@@ -781,6 +778,7 @@ size_t SocketSendData(CNode *pnode)
781778
if (pnode->nSendOffset == data.size()) {
782779
pnode->nSendOffset = 0;
783780
pnode->nSendSize -= data.size();
781+
pnode->fPauseSend = pnode->nSendSize > nSendBufferMaxSize;
784782
it++;
785783
} else {
786784
// could not send full message; stop sending more
@@ -1052,8 +1050,7 @@ void CConnman::ThreadSocketHandler()
10521050
std::vector<CNode*> vNodesCopy = vNodes;
10531051
BOOST_FOREACH(CNode* pnode, vNodesCopy)
10541052
{
1055-
if (pnode->fDisconnect ||
1056-
(pnode->GetRefCount() <= 0 && pnode->vRecvMsg.empty() && pnode->nSendSize == 0))
1053+
if (pnode->fDisconnect)
10571054
{
10581055
// remove from vNodes
10591056
vNodes.erase(remove(vNodes.begin(), vNodes.end(), pnode), vNodes.end());
@@ -1083,13 +1080,9 @@ void CConnman::ThreadSocketHandler()
10831080
TRY_LOCK(pnode->cs_vSend, lockSend);
10841081
if (lockSend)
10851082
{
1086-
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
1087-
if (lockRecv)
1088-
{
10891083
TRY_LOCK(pnode->cs_inventory, lockInv);
10901084
if (lockInv)
10911085
fDelete = true;
1092-
}
10931086
}
10941087
}
10951088
if (fDelete)
@@ -1149,15 +1142,10 @@ void CConnman::ThreadSocketHandler()
11491142
// write buffer in this case before receiving more. This avoids
11501143
// needlessly queueing received data, if the remote peer is not themselves
11511144
// receiving data. This means properly utilizing TCP flow control signalling.
1152-
// * Otherwise, if there is no (complete) message in the receive buffer,
1153-
// or there is space left in the buffer, select() for receiving data.
1154-
// * (if neither of the above applies, there is certainly one message
1155-
// in the receiver buffer ready to be processed).
1156-
// Together, that means that at least one of the following is always possible,
1157-
// so we don't deadlock:
1158-
// * We send some data.
1159-
// * We wait for data to be received (and disconnect after timeout).
1160-
// * We process a message in the buffer (message handler thread).
1145+
// * Otherwise, if there is space left in the receive buffer, select() for
1146+
// receiving data.
1147+
// * Hand off all complete messages to the processor, to be handled without
1148+
// blocking here.
11611149
{
11621150
TRY_LOCK(pnode->cs_vSend, lockSend);
11631151
if (lockSend) {
@@ -1168,10 +1156,7 @@ void CConnman::ThreadSocketHandler()
11681156
}
11691157
}
11701158
{
1171-
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
1172-
if (lockRecv && (
1173-
pnode->vRecvMsg.empty() || !pnode->vRecvMsg.front().complete() ||
1174-
pnode->GetTotalRecvSize() <= GetReceiveFloodSize()))
1159+
if (!pnode->fPauseRecv)
11751160
FD_SET(pnode->hSocket, &fdsetRecv);
11761161
}
11771162
}
@@ -1230,8 +1215,6 @@ void CConnman::ThreadSocketHandler()
12301215
continue;
12311216
if (FD_ISSET(pnode->hSocket, &fdsetRecv) || FD_ISSET(pnode->hSocket, &fdsetError))
12321217
{
1233-
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
1234-
if (lockRecv)
12351218
{
12361219
{
12371220
// typical socket buffer is 8K-64K
@@ -1242,11 +1225,23 @@ void CConnman::ThreadSocketHandler()
12421225
bool notify = false;
12431226
if (!pnode->ReceiveMsgBytes(pchBuf, nBytes, notify))
12441227
pnode->CloseSocketDisconnect();
1245-
if(notify)
1246-
condMsgProc.notify_one();
1247-
pnode->nLastRecv = GetTime();
1248-
pnode->nRecvBytes += nBytes;
12491228
RecordBytesRecv(nBytes);
1229+
if (notify) {
1230+
size_t nSizeAdded = 0;
1231+
auto it(pnode->vRecvMsg.begin());
1232+
for (; it != pnode->vRecvMsg.end(); ++it) {
1233+
if (!it->complete())
1234+
break;
1235+
nSizeAdded += it->vRecv.size() + CMessageHeader::HEADER_SIZE;
1236+
}
1237+
{
1238+
LOCK(pnode->cs_vProcessMsg);
1239+
pnode->vProcessMsg.splice(pnode->vProcessMsg.end(), pnode->vRecvMsg, pnode->vRecvMsg.begin(), it);
1240+
pnode->nProcessQueueSize += nSizeAdded;
1241+
pnode->fPauseRecv = pnode->nProcessQueueSize > nReceiveFloodSize;
1242+
}
1243+
WakeMessageHandler();
1244+
}
12501245
}
12511246
else if (nBytes == 0)
12521247
{
@@ -1280,8 +1275,9 @@ void CConnman::ThreadSocketHandler()
12801275
TRY_LOCK(pnode->cs_vSend, lockSend);
12811276
if (lockSend) {
12821277
size_t nBytes = SocketSendData(pnode);
1283-
if (nBytes)
1278+
if (nBytes) {
12841279
RecordBytesSent(nBytes);
1280+
}
12851281
}
12861282
}
12871283

@@ -1321,8 +1317,14 @@ void CConnman::ThreadSocketHandler()
13211317
}
13221318
}
13231319

1324-
1325-
1320+
void CConnman::WakeMessageHandler()
1321+
{
1322+
{
1323+
std::lock_guard<std::mutex> lock(mutexMsgProc);
1324+
fMsgProcWake = true;
1325+
}
1326+
condMsgProc.notify_one();
1327+
}
13261328

13271329

13281330

@@ -1858,30 +1860,16 @@ void CConnman::ThreadMessageHandler()
18581860
}
18591861
}
18601862

1861-
bool fSleep = true;
1863+
bool fMoreWork = false;
18621864

18631865
BOOST_FOREACH(CNode* pnode, vNodesCopy)
18641866
{
18651867
if (pnode->fDisconnect)
18661868
continue;
18671869

18681870
// Receive messages
1869-
{
1870-
TRY_LOCK(pnode->cs_vRecvMsg, lockRecv);
1871-
if (lockRecv)
1872-
{
1873-
if (!GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc))
1874-
pnode->CloseSocketDisconnect();
1875-
1876-
if (pnode->nSendSize < GetSendBufferSize())
1877-
{
1878-
if (!pnode->vRecvGetData.empty() || (!pnode->vRecvMsg.empty() && pnode->vRecvMsg[0].complete()))
1879-
{
1880-
fSleep = false;
1881-
}
1882-
}
1883-
}
1884-
}
1871+
bool fMoreNodeWork = GetNodeSignals().ProcessMessages(pnode, *this, flagInterruptMsgProc);
1872+
fMoreWork |= (fMoreNodeWork && !pnode->fPauseSend);
18851873
if (flagInterruptMsgProc)
18861874
return;
18871875

@@ -1901,10 +1889,11 @@ void CConnman::ThreadMessageHandler()
19011889
pnode->Release();
19021890
}
19031891

1904-
if (fSleep) {
1905-
std::unique_lock<std::mutex> lock(mutexMsgProc);
1906-
condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100));
1892+
std::unique_lock<std::mutex> lock(mutexMsgProc);
1893+
if (!fMoreWork) {
1894+
condMsgProc.wait_until(lock, std::chrono::steady_clock::now() + std::chrono::milliseconds(100), [this] { return fMsgProcWake; });
19071895
}
1896+
fMsgProcWake = false;
19081897
}
19091898
}
19101899

@@ -2121,7 +2110,7 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c
21212110
nMaxFeeler = connOptions.nMaxFeeler;
21222111

21232112
nSendBufferMaxSize = connOptions.nSendBufferMaxSize;
2124-
nReceiveFloodSize = connOptions.nSendBufferMaxSize;
2113+
nReceiveFloodSize = connOptions.nReceiveFloodSize;
21252114

21262115
nMaxOutboundLimit = connOptions.nMaxOutboundLimit;
21272116
nMaxOutboundTimeframe = connOptions.nMaxOutboundTimeframe;
@@ -2182,6 +2171,11 @@ bool CConnman::Start(CScheduler& scheduler, std::string& strNodeError, Options c
21822171
interruptNet.reset();
21832172
flagInterruptMsgProc = false;
21842173

2174+
{
2175+
std::unique_lock<std::mutex> lock(mutexMsgProc);
2176+
fMsgProcWake = false;
2177+
}
2178+
21852179
// Send and receive from sockets, accept connections
21862180
threadSocketHandler = std::thread(&TraceThread<std::function<void()> >, "net", std::function<void()>(std::bind(&CConnman::ThreadSocketHandler, this)));
21872181

@@ -2613,6 +2607,9 @@ CNode::CNode(NodeId idIn, ServiceFlags nLocalServicesIn, int nMyStartingHeightIn
26132607
minFeeFilter = 0;
26142608
lastSentFeeFilter = 0;
26152609
nextSendTimeFeeFilter = 0;
2610+
fPauseRecv = false;
2611+
fPauseSend = false;
2612+
nProcessQueueSize = 0;
26162613

26172614
BOOST_FOREACH(const std::string &msg, getAllNetMessageTypes())
26182615
mapRecvBytesPerMsgCmd[msg] = 0;
@@ -2692,6 +2689,8 @@ void CConnman::PushMessage(CNode* pnode, CSerializedNetMsg&& msg)
26922689
pnode->mapSendBytesPerMsgCmd[msg.command] += nTotalSize;
26932690
pnode->nSendSize += nTotalSize;
26942691

2692+
if (pnode->nSendSize > nSendBufferMaxSize)
2693+
pnode->fPauseSend = true;
26952694
pnode->vSendMsg.push_back(std::move(serializedHeader));
26962695
if (nMessageSize)
26972696
pnode->vSendMsg.push_back(std::move(msg.data));

src/net.h

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,7 @@ class CConnman
327327
/** Get a unique deterministic randomizer. */
328328
CSipHasher GetDeterministicRandomizer(uint64_t id);
329329

330+
unsigned int GetReceiveFloodSize() const;
330331
private:
331332
struct ListenSocket {
332333
SOCKET socket;
@@ -343,6 +344,8 @@ class CConnman
343344
void ThreadSocketHandler();
344345
void ThreadDNSAddressSeed();
345346

347+
void WakeMessageHandler();
348+
346349
uint64_t CalculateKeyedNetGroup(const CAddress& ad);
347350

348351
CNode* FindNode(const CNetAddr& ip);
@@ -358,6 +361,7 @@ class CConnman
358361

359362
NodeId GetNewNodeId();
360363

364+
size_t SocketSendData(CNode *pnode);
361365
//!check is the banlist has unwritten changes
362366
bool BannedSetIsDirty();
363367
//!set the "dirty" flag for the banlist
@@ -368,8 +372,6 @@ class CConnman
368372
void DumpData();
369373
void DumpBanlist();
370374

371-
unsigned int GetReceiveFloodSize() const;
372-
373375
// Network stats
374376
void RecordBytesRecv(uint64_t bytes);
375377
void RecordBytesSent(uint64_t bytes);
@@ -428,6 +430,9 @@ class CConnman
428430
/** SipHasher seeds for deterministic randomness */
429431
const uint64_t nSeed0, nSeed1;
430432

433+
/** flag for waking the message processor. */
434+
bool fMsgProcWake;
435+
431436
std::condition_variable condMsgProc;
432437
std::mutex mutexMsgProc;
433438
std::atomic<bool> flagInterruptMsgProc;
@@ -445,7 +450,6 @@ void Discover(boost::thread_group& threadGroup);
445450
void MapPort(bool fUseUPnP);
446451
unsigned short GetListenPort();
447452
bool BindListenPort(const CService &bindAddr, std::string& strError, bool fWhitelisted = false);
448-
size_t SocketSendData(CNode *pnode);
449453

450454
struct CombinerAll
451455
{
@@ -610,11 +614,13 @@ class CNode
610614
std::deque<std::vector<unsigned char>> vSendMsg;
611615
CCriticalSection cs_vSend;
612616

617+
CCriticalSection cs_vProcessMsg;
618+
std::list<CNetMessage> vProcessMsg;
619+
size_t nProcessQueueSize;
620+
613621
std::deque<CInv> vRecvGetData;
614-
std::deque<CNetMessage> vRecvMsg;
615-
CCriticalSection cs_vRecvMsg;
616622
uint64_t nRecvBytes;
617-
int nRecvVersion;
623+
std::atomic<int> nRecvVersion;
618624

619625
int64_t nLastSend;
620626
int64_t nLastRecv;
@@ -650,6 +656,8 @@ class CNode
650656
const NodeId id;
651657

652658
const uint64_t nKeyedNetGroup;
659+
std::atomic_bool fPauseRecv;
660+
std::atomic_bool fPauseSend;
653661
protected:
654662

655663
mapMsgCmdSize mapSendBytesPerMsgCmd;
@@ -723,6 +731,7 @@ class CNode
723731
const ServiceFlags nLocalServices;
724732
const int nMyStartingHeight;
725733
int nSendVersion;
734+
std::list<CNetMessage> vRecvMsg; // Used only by SocketHandler thread
726735
public:
727736

728737
NodeId GetId() const {
@@ -743,24 +752,15 @@ class CNode
743752
return nRefCount;
744753
}
745754

746-
// requires LOCK(cs_vRecvMsg)
747-
unsigned int GetTotalRecvSize()
748-
{
749-
unsigned int total = 0;
750-
BOOST_FOREACH(const CNetMessage &msg, vRecvMsg)
751-
total += msg.vRecv.size() + 24;
752-
return total;
753-
}
754-
755-
// requires LOCK(cs_vRecvMsg)
756755
bool ReceiveMsgBytes(const char *pch, unsigned int nBytes, bool& complete);
757756

758-
// requires LOCK(cs_vRecvMsg)
759757
void SetRecvVersion(int nVersionIn)
760758
{
761759
nRecvVersion = nVersionIn;
762-
BOOST_FOREACH(CNetMessage &msg, vRecvMsg)
763-
msg.SetVersion(nVersionIn);
760+
}
761+
int GetRecvVersion()
762+
{
763+
return nRecvVersion;
764764
}
765765
void SetSendVersion(int nVersionIn)
766766
{

0 commit comments

Comments
 (0)