diff --git a/src/node/interfaces.cpp b/src/node/interfaces.cpp index cf21787d7d1..b5c74a97f6d 100644 --- a/src/node/interfaces.cpp +++ b/src/node/interfaces.cpp @@ -971,32 +971,7 @@ public: std::optional waitTipChanged(uint256 current_tip, MillisecondsDouble timeout) override { - Assume(timeout >= 0ms); // No internal callers should use a negative timeout - if (timeout < 0ms) timeout = 0ms; - if (timeout > std::chrono::years{100}) timeout = std::chrono::years{100}; // Upper bound to avoid UB in std::chrono - auto deadline{std::chrono::steady_clock::now() + timeout}; - { - WAIT_LOCK(notifications().m_tip_block_mutex, lock); - // For callers convenience, wait longer than the provided timeout - // during startup for the tip to be non-null. That way this function - // always returns valid tip information when possible and only - // returns null when shutting down, not when timing out. - notifications().m_tip_block_cv.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(notifications().m_tip_block_mutex) { - return notifications().TipBlock() || chainman().m_interrupt; - }); - if (chainman().m_interrupt) return {}; - // At this point TipBlock is set, so continue to wait until it is - // different then `current_tip` provided by caller. - notifications().m_tip_block_cv.wait_until(lock, deadline, [&]() EXCLUSIVE_LOCKS_REQUIRED(notifications().m_tip_block_mutex) { - return Assume(notifications().TipBlock()) != current_tip || chainman().m_interrupt; - }); - } - - if (chainman().m_interrupt) return {}; - - // Must release m_tip_block_mutex before getTip() locks cs_main, to - // avoid deadlocks. - return getTip(); + return WaitTipChanged(chainman(), notifications(), current_tip, timeout); } std::unique_ptr createNewBlock(const BlockCreateOptions& options) override diff --git a/src/node/miner.cpp b/src/node/miner.cpp index 426e065ff21..70567cf5b68 100644 --- a/src/node/miner.cpp +++ b/src/node/miner.cpp @@ -547,4 +547,33 @@ std::optional GetTip(ChainstateManager& chainman) if (!tip) return {}; return BlockRef{tip->GetBlockHash(), tip->nHeight}; } + +std::optional WaitTipChanged(ChainstateManager& chainman, KernelNotifications& kernel_notifications, const uint256& current_tip, MillisecondsDouble& timeout) +{ + Assume(timeout >= 0ms); // No internal callers should use a negative timeout + if (timeout < 0ms) timeout = 0ms; + if (timeout > std::chrono::years{100}) timeout = std::chrono::years{100}; // Upper bound to avoid UB in std::chrono + auto deadline{std::chrono::steady_clock::now() + timeout}; + { + WAIT_LOCK(kernel_notifications.m_tip_block_mutex, lock); + // For callers convenience, wait longer than the provided timeout + // during startup for the tip to be non-null. That way this function + // always returns valid tip information when possible and only + // returns null when shutting down, not when timing out. + kernel_notifications.m_tip_block_cv.wait(lock, [&]() EXCLUSIVE_LOCKS_REQUIRED(kernel_notifications.m_tip_block_mutex) { + return kernel_notifications.TipBlock() || chainman.m_interrupt; + }); + if (chainman.m_interrupt) return {}; + // At this point TipBlock is set, so continue to wait until it is + // different then `current_tip` provided by caller. + kernel_notifications.m_tip_block_cv.wait_until(lock, deadline, [&]() EXCLUSIVE_LOCKS_REQUIRED(kernel_notifications.m_tip_block_mutex) { + return Assume(kernel_notifications.TipBlock()) != current_tip || chainman.m_interrupt; + }); + } + if (chainman.m_interrupt) return {}; + + // Must release m_tip_block_mutex before getTip() locks cs_main, to + // avoid deadlocks. + return GetTip(chainman); +} } // namespace node diff --git a/src/node/miner.h b/src/node/miner.h index d0ae8312fb5..5073d69bb1b 100644 --- a/src/node/miner.h +++ b/src/node/miner.h @@ -251,6 +251,10 @@ std::unique_ptr WaitAndCreateNewBlock(ChainstateManager& chainma /* Locks cs_main and returns the block hash and block height of the active chain if it exists; otherwise, returns nullopt.*/ std::optional GetTip(ChainstateManager& chainman); + +/* Waits for the connected tip to change until timeout has elapsed. During node initialization, this will wait until the tip is connected (regardless of `timeout`). + * Returns the current tip, or nullopt if the node is shutting down. */ +std::optional WaitTipChanged(ChainstateManager& chainman, KernelNotifications& kernel_notifications, const uint256& current_tip, MillisecondsDouble& timeout); } // namespace node #endif // BITCOIN_NODE_MINER_H