Skip to content

Commit a504426

Browse files
authored
btc(v36): complete twin-parity — think-independent 10s re-advert timer leg
Mirror the LTC timer leg from #104/e01c1606. The event leg (readvertise_best on think work-refresh + clean_tracker best-change) goes dead exactly when think() wedges -- the livelock-adjacent scenario this fix family targets. Arm a one-shot core::Timer(10s) -> readvertise_best() in the run_think IO-phase, fired exactly once when the verified chain first becomes non-empty, so a peer that handshook during the empty window can still ingest our chain even if a later think() cycle wedges. Composes with the merged #97 think-watchdog: timer runs on the IO thread, pure reads, broadcast stays try_to_lock, no new mutex. Co-authored-by: frstrtr <frstrtr@users.noreply.github.com>
1 parent 47c7a02 commit a504426

2 files changed

Lines changed: 20 additions & 0 deletions

File tree

src/impl/btc/node.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1624,6 +1624,21 @@ void NodeImpl::run_think()
16241624
<< " verified_heads=" << m_tracker.verified.get_heads().size();
16251625
}
16261626

1627+
// ROOT-2: fire ONE think-independent delayed re-advert when the
1628+
// verified chain first becomes non-empty. Covers peers that
1629+
// handshook during the empty window and so never see a best-change
1630+
// event. The one-shot timer runs on the IO thread, so it still
1631+
// fires even if a later think() cycle wedges (composes with the
1632+
// #97 think-watchdog). Pure reads; broadcast stays try_to_lock.
1633+
if (m_verified_was_empty && m_tracker.verified.size() > 0) {
1634+
m_verified_was_empty = false;
1635+
if (!m_readvert_timer)
1636+
m_readvert_timer = std::make_unique<core::Timer>(m_context, false);
1637+
m_readvert_timer->start(10, [this]() { readvertise_best(); });
1638+
LOG_INFO << "[readvertise] verified chain populated — scheduled "
1639+
"10s re-advert (ROOT-2)";
1640+
}
1641+
16271642
// Drain share batches queued while think() held the mutex
16281643
LOG_INFO << "[ASYNC-THINK] IO-phase: draining " << m_pending_adds.size()
16291644
<< " pending batches, peers=" << m_peers.size();

src/impl/btc/node.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,7 @@ class NodeImpl : public pool::BaseNode<btc::Config, btc::ShareChain, btc::Peer>
517517
size_t m_max_peers = 30;
518518
size_t m_target_outbound_peers = DEFAULT_TARGET_OUTBOUND_PEERS;
519519
std::unique_ptr<core::Timer> m_connect_timer;
520+
std::unique_ptr<core::Timer> m_readvert_timer; // one-shot ROOT-2 re-advert
520521
std::set<NetService> m_pending_outbound; // addresses currently being dialed
521522
std::set<NetService> m_outbound_addrs; // successfully connected outbound peers
522523

@@ -552,6 +553,10 @@ class NodeImpl : public pool::BaseNode<btc::Config, btc::ShareChain, btc::Peer>
552553
// Cached best share hash from the most recent think() cycle
553554
uint256 m_best_share_hash;
554555

556+
// ROOT-2: fire exactly one delayed re-advert when the verified chain
557+
// first transitions from empty to non-empty (peers that handshook
558+
// during the empty window otherwise never see a best-change event).
559+
bool m_verified_was_empty = true;
555560

556561
// Cache of original raw serialized bytes keyed by share hash.
557562
// Used for relay so we send the exact bytes we received, avoiding

0 commit comments

Comments
 (0)