@@ -219,6 +219,13 @@ struct Peer {
219
219
/* * This peer's reported block height when we connected */
220
220
std::atomic<int > m_starting_height{-1 };
221
221
222
+ /* * The pong reply we're expecting, or 0 if no pong expected. */
223
+ std::atomic<uint64_t > nPingNonceSent{0 };
224
+ /* * When the last ping was sent, or 0 if no ping was ever sent */
225
+ std::atomic<std::chrono::microseconds> m_ping_start{0us};
226
+ /* * Whether a ping has been requested by the user */
227
+ std::atomic<bool > fPingQueued {false };
228
+
222
229
/* * Set of txids to reconsider once their parent transactions have been accepted **/
223
230
std::set<uint256> m_orphan_work_set GUARDED_BY (g_cs_orphans);
224
231
@@ -256,6 +263,7 @@ class PeerManagerImpl final : public PeerManager
256
263
void CheckForStaleTipAndEvictPeers () override ;
257
264
bool GetNodeStateStats (NodeId nodeid, CNodeStateStats& stats) override ;
258
265
bool IgnoresIncomingTxs () override { return m_ignore_incoming_txs; }
266
+ void SendPings () override ;
259
267
void SetBestHeight (int height) override { m_best_height = height; };
260
268
void Misbehaving (const NodeId pnode, const int howmuch, const std::string& message) override ;
261
269
void ProcessMessage (CNode& pfrom, const std::string& msg_type, CDataStream& vRecv,
@@ -326,7 +334,7 @@ class PeerManagerImpl final : public PeerManager
326
334
327
335
/* * Send a ping message every PING_INTERVAL or if requested via RPC. May
328
336
* mark the peer to be disconnected if a ping has timed out. */
329
- void MaybeSendPing (CNode& node_to);
337
+ void MaybeSendPing (CNode& node_to, Peer& peer );
330
338
331
339
const CChainParams& m_chainparams;
332
340
CConnman& m_connman;
@@ -1093,6 +1101,18 @@ bool PeerManagerImpl::GetNodeStateStats(NodeId nodeid, CNodeStateStats &stats)
1093
1101
PeerRef peer = GetPeerRef (nodeid);
1094
1102
if (peer == nullptr ) return false ;
1095
1103
stats.m_starting_height = peer->m_starting_height ;
1104
+ // It is common for nodes with good ping times to suddenly become lagged,
1105
+ // due to a new block arriving or other large transfer.
1106
+ // Merely reporting pingtime might fool the caller into thinking the node was still responsive,
1107
+ // since pingtime does not update until the ping is complete, which might take a while.
1108
+ // So, if a ping is taking an unusually long time in flight,
1109
+ // the caller can immediately detect that this is happening.
1110
+ std::chrono::microseconds ping_wait{0 };
1111
+ if ((0 != peer->nPingNonceSent ) && (0 != peer->m_ping_start .load ().count ())) {
1112
+ ping_wait = GetTime<std::chrono::microseconds>() - peer->m_ping_start .load ();
1113
+ }
1114
+
1115
+ stats.m_ping_wait_usec = count_microseconds (ping_wait);
1096
1116
1097
1117
return true ;
1098
1118
}
@@ -1632,6 +1652,12 @@ bool static AlreadyHaveBlock(const uint256& block_hash) EXCLUSIVE_LOCKS_REQUIRED
1632
1652
return g_chainman.m_blockman .LookupBlockIndex (block_hash) != nullptr ;
1633
1653
}
1634
1654
1655
+ void PeerManagerImpl::SendPings ()
1656
+ {
1657
+ LOCK (m_peer_mutex);
1658
+ for (auto & it : m_peer_map) it.second ->fPingQueued = true ;
1659
+ }
1660
+
1635
1661
void RelayTransaction (const uint256& txid, const uint256& wtxid, const CConnman& connman)
1636
1662
{
1637
1663
connman.ForEachNode ([&txid, &wtxid](CNode* pnode) EXCLUSIVE_LOCKS_REQUIRED (::cs_main) {
@@ -3842,15 +3868,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
3842
3868
vRecv >> nonce;
3843
3869
3844
3870
// Only process pong message if there is an outstanding ping (old ping without nonce should never pong)
3845
- if (pfrom. nPingNonceSent != 0 ) {
3846
- if (nonce == pfrom. nPingNonceSent ) {
3871
+ if (peer-> nPingNonceSent != 0 ) {
3872
+ if (nonce == peer-> nPingNonceSent ) {
3847
3873
// Matching pong received, this ping is no longer outstanding
3848
3874
bPingFinished = true ;
3849
- const auto ping_time = ping_end - pfrom. m_ping_start .load ();
3875
+ const auto ping_time = ping_end - peer-> m_ping_start .load ();
3850
3876
if (ping_time.count () >= 0 ) {
3851
- // Successful ping time measurement, replace previous
3852
- pfrom.nPingUsecTime = count_microseconds (ping_time);
3853
- pfrom.nMinPingUsecTime = std::min (pfrom.nMinPingUsecTime .load (), count_microseconds (ping_time));
3877
+ // Let connman know about this successful ping-pong
3878
+ pfrom.PongReceived (ping_time);
3854
3879
} else {
3855
3880
// This should never happen
3856
3881
sProblem = " Timing mishap" ;
@@ -3877,12 +3902,12 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
3877
3902
LogPrint (BCLog::NET, " pong peer=%d: %s, %x expected, %x received, %u bytes\n " ,
3878
3903
pfrom.GetId (),
3879
3904
sProblem ,
3880
- pfrom. nPingNonceSent ,
3905
+ peer-> nPingNonceSent ,
3881
3906
nonce,
3882
3907
nAvail);
3883
3908
}
3884
3909
if (bPingFinished) {
3885
- pfrom. nPingNonceSent = 0 ;
3910
+ peer-> nPingNonceSent = 0 ;
3886
3911
}
3887
3912
return ;
3888
3913
}
@@ -4296,28 +4321,28 @@ void PeerManagerImpl::CheckForStaleTipAndEvictPeers()
4296
4321
}
4297
4322
}
4298
4323
4299
- void PeerManagerImpl::MaybeSendPing (CNode& node_to)
4324
+ void PeerManagerImpl::MaybeSendPing (CNode& node_to, Peer& peer )
4300
4325
{
4301
4326
// Use mockable time for ping timeouts.
4302
4327
// This means that setmocktime may cause pings to time out.
4303
4328
auto now = GetTime<std::chrono::microseconds>();
4304
4329
4305
- if (m_connman.RunInactivityChecks (node_to) && node_to .nPingNonceSent &&
4306
- now > node_to .m_ping_start .load () + std::chrono::seconds{TIMEOUT_INTERVAL}) {
4307
- LogPrint (BCLog::NET, " ping timeout: %fs peer=%d\n " , 0.000001 * count_microseconds (now - node_to .m_ping_start .load ()), node_to. GetId () );
4330
+ if (m_connman.RunInactivityChecks (node_to) && peer .nPingNonceSent &&
4331
+ now > peer .m_ping_start .load () + std::chrono::seconds{TIMEOUT_INTERVAL}) {
4332
+ LogPrint (BCLog::NET, " ping timeout: %fs peer=%d\n " , 0.000001 * count_microseconds (now - peer .m_ping_start .load ()), peer. m_id );
4308
4333
node_to.fDisconnect = true ;
4309
4334
return ;
4310
4335
}
4311
4336
4312
4337
const CNetMsgMaker msgMaker (node_to.GetCommonVersion ());
4313
4338
bool pingSend = false ;
4314
4339
4315
- if (node_to .fPingQueued ) {
4340
+ if (peer .fPingQueued ) {
4316
4341
// RPC ping request by user
4317
4342
pingSend = true ;
4318
4343
}
4319
4344
4320
- if (node_to .nPingNonceSent == 0 && now > node_to .m_ping_start .load () + PING_INTERVAL) {
4345
+ if (peer .nPingNonceSent == 0 && now > peer .m_ping_start .load () + PING_INTERVAL) {
4321
4346
// Ping automatically sent as a latency probe & keepalive.
4322
4347
pingSend = true ;
4323
4348
}
@@ -4327,14 +4352,14 @@ void PeerManagerImpl::MaybeSendPing(CNode& node_to)
4327
4352
while (nonce == 0 ) {
4328
4353
GetRandBytes ((unsigned char *)&nonce, sizeof (nonce));
4329
4354
}
4330
- node_to .fPingQueued = false ;
4331
- node_to .m_ping_start = now;
4355
+ peer .fPingQueued = false ;
4356
+ peer .m_ping_start = now;
4332
4357
if (node_to.GetCommonVersion () > BIP0031_VERSION) {
4333
- node_to .nPingNonceSent = nonce;
4358
+ peer .nPingNonceSent = nonce;
4334
4359
m_connman.PushMessage (&node_to, msgMaker.Make (NetMsgType::PING, nonce));
4335
4360
} else {
4336
4361
// Peer is too old to support ping command with nonce, pong will never arrive.
4337
- node_to .nPingNonceSent = 0 ;
4362
+ peer .nPingNonceSent = 0 ;
4338
4363
m_connman.PushMessage (&node_to, msgMaker.Make (NetMsgType::PING));
4339
4364
}
4340
4365
}
@@ -4378,7 +4403,7 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
4378
4403
// If we get here, the outgoing message serialization version is set and can't change.
4379
4404
const CNetMsgMaker msgMaker (pto->GetCommonVersion ());
4380
4405
4381
- MaybeSendPing (*pto);
4406
+ MaybeSendPing (*pto, *peer );
4382
4407
4383
4408
// MaybeSendPing may have marked peer for disconnection
4384
4409
if (pto->fDisconnect ) return true ;
0 commit comments