Not sure about an infinite timeout for ASSERT_DEBUG_LOG. For example, this can easily be hit when the log string is modified by a developer, turning a (doc) change in one place into a hard to debug unit test issue.
I agree. I will stick to a meaningful message after a timeout instead of infinite wait (without a message).
I know that it is possible to hit an infinite runtime in any other place, but that would generally mean a major fault or injected bug elsewhere. For example, if the scheduler thread is stopped and one called SyncWithValidationInterfaceQueue()...
I agree with that too. But the parallel with SyncWithValidationInterfaceQueue() is not very close to the tests in this PR because SyncWithValidationInterfaceQueue() does not provide fine-grained sync points, see below what I mean.
It would be good to explain why you need the debug log to sync and why it is not (easily) possible to just sync in this pull.
It is possible to not use the debug log. Here is how it would look like:
<details>
<summary>[patch] use fine-grained sync points in high-level code</summary>
diff --git i/src/net_processing.cpp w/src/net_processing.cpp
index 1c73ca4eb4..35301a6a2d 100644
--- i/src/net_processing.cpp
+++ w/src/net_processing.cpp
@@ -3529,12 +3529,13 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// At this point, the outgoing message serialization version can't change.
const CNetMsgMaker msgMaker(pfrom.GetCommonVersion());
if (msg_type == NetMsgType::VERACK) {
if (pfrom.fSuccessfullyConnected) {
LogPrint(BCLog::NET, "ignoring redundant verack message from peer=%d\n", pfrom.GetId());
+ g_stage_redundant_verack.post();
return;
}
if (!pfrom.IsInboundConn()) {
const auto mapped_as{m_connman.GetMappedAS(pfrom.addr)};
LogPrintf("New outbound peer connected: version: %d, blocks=%d, peer=%d%s%s (%s)\n",
@@ -3781,12 +3782,14 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
}
peer->m_addr_processed += num_proc;
peer->m_addr_rate_limited += num_rate_limit;
LogPrint(BCLog::NET, "Received addr: %u addresses (%u processed, %u rate-limited) from peer=%d\n",
vAddr.size(), num_proc, num_rate_limit, pfrom.GetId());
+ g_stage_received_addr.post();
+
m_addrman.Add(vAddrOk, pfrom.addr, 2h);
if (vAddr.size() < 1000) peer->m_getaddr_sent = false;
// AddrFetch: Require multiple addresses to avoid disconnecting on self-announcements
if (pfrom.IsAddrFetchConn() && vAddr.size() > 1) {
LogPrint(BCLog::NET, "addrfetch connection completed peer=%d; disconnecting\n", pfrom.GetId());
@@ -3948,12 +3951,15 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
// Send the rest of the chain
if (pindex)
pindex = m_chainman.ActiveChain().Next(pindex);
int nLimit = 500;
LogPrint(BCLog::NET, "getblocks %d to %s limit %d from peer=%d\n", (pindex ? pindex->nHeight : -1), hashStop.IsNull() ? "end" : hashStop.ToString(), nLimit, pfrom.GetId());
+ if (pindex == nullptr && hashStop.IsNull()) {
+ g_stage_getblocks_all.post();
+ }
for (; pindex; pindex = m_chainman.ActiveChain().Next(pindex))
{
if (pindex->GetBlockHash() == hashStop)
{
LogPrint(BCLog::NET, " getblocks stopping at %d %s\n", pindex->nHeight, pindex->GetBlockHash().ToString());
break;
@@ -4765,26 +4771,31 @@ void PeerManagerImpl::ProcessMessage(CNode& pfrom, const std::string& msg_type,
} else {
// This should never happen
sProblem = "Timing mishap";
}
} else {
// Nonce mismatches are normal when pings are overlapping
- sProblem = "Nonce mismatch";
if (nonce == 0) {
// This is most likely a bug in another implementation somewhere; cancel this ping
bPingFinished = true;
sProblem = "Nonce zero";
+ g_stage_ping_nonce_zero.post();
+ } else {
+ sProblem = "Nonce mismatch";
+ g_stage_ping_nonce_mismatch.post();
}
}
} else {
sProblem = "Unsolicited pong without ping";
+ g_stage_unsolicited_pong_without_ping.post();
}
} else {
// This is most likely a bug in another implementation somewhere; cancel this ping
bPingFinished = true;
sProblem = "Short payload";
+ g_stage_ping_short_payload.post();
}
if (!(sProblem.empty())) {
LogPrint(BCLog::NET, "pong peer=%d: %s, %x expected, %x received, %u bytes\n",
pfrom.GetId(),
sProblem,
@@ -5259,12 +5270,13 @@ void PeerManagerImpl::MaybeSendPing(CNode& node_to, Peer& peer, std::chrono::mic
m_connman.PushMessage(&node_to, msgMaker.Make(NetMsgType::PING, nonce));
} else {
// Peer is too old to support ping command with nonce, pong will never arrive.
peer.m_ping_nonce_sent = 0;
m_connman.PushMessage(&node_to, msgMaker.Make(NetMsgType::PING));
}
+ g_stage_sending_ping.post();
}
}
void PeerManagerImpl::MaybeSendAddr(CNode& node, Peer& peer, std::chrono::microseconds current_time)
{
// Nothing to do for non-address-relay peers
@@ -5928,6 +5940,15 @@ bool PeerManagerImpl::SendMessages(CNode* pto)
if (!vGetData.empty())
m_connman.PushMessage(pto, msgMaker.Make(NetMsgType::GETDATA, vGetData));
} // release cs_main
MaybeSendFeefilter(*pto, *peer, current_time);
return true;
}
+
+CSemaphore g_stage_received_addr{0};
+CSemaphore g_stage_getblocks_all{0};
+CSemaphore g_stage_sending_ping{0};
+CSemaphore g_stage_ping_short_payload{0};
+CSemaphore g_stage_unsolicited_pong_without_ping{0};
+CSemaphore g_stage_ping_nonce_mismatch{0};
+CSemaphore g_stage_ping_nonce_zero{0};
+CSemaphore g_stage_redundant_verack{0};
diff --git i/src/net_processing.h w/src/net_processing.h
index 1bd2b1e8ca..de4a12705f 100644
--- i/src/net_processing.h
+++ w/src/net_processing.h
@@ -91,7 +91,16 @@ public:
const std::chrono::microseconds time_received, const std::atomic<bool>& interruptMsgProc) EXCLUSIVE_LOCKS_REQUIRED(g_msgproc_mutex) = 0;
/** This function is used for testing the stale tip eviction logic, see denialofservice_tests.cpp */
virtual void UpdateLastBlockAnnounceTime(NodeId node, int64_t time_in_seconds) = 0;
};
+extern CSemaphore g_stage_received_addr;
+extern CSemaphore g_stage_getblocks_all;
+extern CSemaphore g_stage_sending_ping;
+extern CSemaphore g_stage_ping_short_payload;
+extern CSemaphore g_stage_unsolicited_pong_without_ping;
+extern CSemaphore g_stage_ping_nonce_mismatch;
+extern CSemaphore g_stage_ping_nonce_zero;
+extern CSemaphore g_stage_redundant_verack;
+
#endif // BITCOIN_NET_PROCESSING_H
diff --git i/src/test/net_msg_tests.cpp w/src/test/net_msg_tests.cpp
index 49c97c1440..17bd74cb3a 100644
--- i/src/test/net_msg_tests.cpp
+++ w/src/test/net_msg_tests.cpp
@@ -63,25 +63,27 @@ BOOST_AUTO_TEST_CASE(initial_messages_exchange)
BOOST_AUTO_TEST_CASE(addr)
{
const auto pipes{m_sockets_pipes.PopFront()};
std::vector<CAddress> addresses{5};
- ASSERT_DEBUG_LOG_WAIT(strprintf("Received addr: %u addresses", addresses.size()), 30s);
+ BOOST_REQUIRE(!g_stage_received_addr.try_wait());
pipes->recv.PushNetMsg(NetMsgType::ADDRV2, addresses);
+ g_stage_received_addr.wait();
}
BOOST_AUTO_TEST_CASE(getblocks)
{
const auto pipes{m_sockets_pipes.PopFront()};
std::vector<uint256> hashes{5};
CBlockLocator block_locator{std::move(hashes)};
uint256 hash_stop;
- ASSERT_DEBUG_LOG_WAIT("getblocks -1 to end", 30s);
+ BOOST_REQUIRE(!g_stage_getblocks_all.try_wait());
pipes->recv.PushNetMsg(NetMsgType::GETBLOCKS, block_locator, hash_stop);
+ g_stage_getblocks_all.wait();
}
BOOST_AUTO_TEST_CASE(ping)
{
const auto pipes{m_sockets_pipes.PopFront()};
@@ -132,14 +134,16 @@ BOOST_AUTO_TEST_CASE(ping)
}
}
};
auto SendPing = [&]() {
{
- ASSERT_DEBUG_LOG_WAIT("sending ping", 30s);
+ while (g_stage_sending_ping.try_wait())
+ ;
SetMockTime(GetMockTime() + PING_INTERVAL + 1s);
+ g_stage_sending_ping.wait();
}
return GetPingNonceSent();
};
BOOST_TEST_MESSAGE("Ensure initial messages exchange has completed with the sending of a ping "
"with nonce != 0 and the ping stats indicate a pending ping.");
@@ -147,37 +151,41 @@ BOOST_AUTO_TEST_CASE(ping)
auto time_elapsed = 1s;
SetMockTime(GetMockTime() + time_elapsed);
WaitForPingStats(/*min=*/std::chrono::microseconds::max(), /*last=*/0us, /*wait=*/time_elapsed);
BOOST_TEST_MESSAGE("Check that receiving a PONG without nonce cancels our PING");
{
- ASSERT_DEBUG_LOG_WAIT("Short payload", 30s);
+ BOOST_REQUIRE(!g_stage_ping_short_payload.try_wait());
pipes->recv.PushNetMsg(NetMsgType::PONG);
+ g_stage_ping_short_payload.wait();
}
WaitForPingStats(/*min=*/std::chrono::microseconds::max(), /*last=*/0us, /*wait=*/0us);
BOOST_TEST_MESSAGE("Check that receiving an unrequested PONG is logged and ignored");
{
- ASSERT_DEBUG_LOG_WAIT("Unsolicited pong without ping", 30s);
+ BOOST_REQUIRE(!g_stage_unsolicited_pong_without_ping.try_wait());
pipes->recv.PushNetMsg(NetMsgType::PONG, /*nonce=*/uint64_t{0});
+ g_stage_unsolicited_pong_without_ping.wait();
}
BOOST_TEST_MESSAGE("Check that receiving a PONG with the wrong nonce does not cancel our PING");
uint64_t nonce{SendPing()};
{
- ASSERT_DEBUG_LOG_WAIT("Nonce mismatch", 30s);
+ BOOST_REQUIRE(!g_stage_ping_nonce_mismatch.try_wait());
pipes->recv.PushNetMsg(NetMsgType::PONG, nonce + 1);
+ g_stage_ping_nonce_mismatch.wait();
}
time_elapsed = 5s;
SetMockTime(GetMockTime() + time_elapsed);
WaitForPingStats(/*min=*/std::chrono::microseconds::max(), /*last=*/0us, /*wait=*/time_elapsed);
BOOST_TEST_MESSAGE("Check that receiving a PONG with nonce=0 cancels our PING");
{
- ASSERT_DEBUG_LOG_WAIT("Nonce zero", 30s);
+ BOOST_REQUIRE(!g_stage_ping_nonce_zero.try_wait());
pipes->recv.PushNetMsg(NetMsgType::PONG, /*nonce=*/uint64_t{0});
+ g_stage_ping_nonce_zero.wait();
}
WaitForPingStats(/*min=*/std::chrono::microseconds::max(), /*last=*/0us, /*wait=*/0us);
BOOST_TEST_MESSAGE("Check that receiving a PONG with the correct nonce cancels our PING");
nonce = SendPing();
time_elapsed = 5s;
@@ -187,11 +195,12 @@ BOOST_AUTO_TEST_CASE(ping)
}
BOOST_AUTO_TEST_CASE(redundant_verack)
{
const auto pipes{m_sockets_pipes.PopFront()};
- ASSERT_DEBUG_LOG_WAIT("ignoring redundant verack message", 30s);
+ BOOST_REQUIRE(!g_stage_redundant_verack.try_wait());
pipes->recv.PushNetMsg(NetMsgType::VERACK);
+ g_stage_redundant_verack.wait();
}
BOOST_AUTO_TEST_SUITE_END()
</details>
I could be missing some easier alternative. Let me know if you have something better in mind.