init: wake genesis wait after ImportBlocks() returns

If shutdown interrupts ImportBlocks() before genesis activation,
no blockTip notification is sent. The wait predicate allows
shutdown to occur, but the condition variable is never
notified, so init can remain stuck waiting for genesis activation.

Notify the tip condition variable after ImportBlocks() returns so
interrupted imports wake the wait and let it observe the shutdown request.

Add test coverage for interrupting startup after reindex block files import
begins, which exercises the path where ImportBlocks() can return before genesis
activation.
This commit is contained in:
ismaelsadeeq
2026-07-03 15:28:29 +01:00
parent 0a1bbec688
commit c1313b199f
2 changed files with 14 additions and 3 deletions

View File

@@ -2045,10 +2045,16 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
}
/// \anchor initload
node.background_init_thread = std::thread(&util::TraceThread, "initload", [=, &chainman, &args, &node] {
node.background_init_thread = std::thread(&util::TraceThread, "initload", [=, &chainman, &args, &kernel_notifications, &node] {
ScheduleBatchPriority();
// Import blocks and ActivateBestChain()
ImportBlocks(chainman, vImportFiles);
// An interrupted import may return without activating genesis. Wake
// the init thread's genesis wait, which is otherwise only notified
// on blockTip, and that never fires when the import was interrupted
// before activating genesis. This wakeup lets the wait observe the
// shutdown request.
WITH_LOCK(kernel_notifications.m_tip_block_mutex, kernel_notifications.m_tip_block_cv.notify_all());
WITH_LOCK(::cs_main, chainman.UpdateIBDStatus());
if (args.GetBoolArg("-stopafterblockimport", DEFAULT_STOPAFTERBLOCKIMPORT)) {
LogInfo("Stopping after block import");

View File

@@ -63,6 +63,7 @@ class InitTest(BitcoinTestFramework):
node.process.terminate()
assert_equal(0, node.process.wait())
reindex_log_line = b'Reindexing block file blk00000.dat'
lines_to_terminate_after = [
b'Validating signatures for all blocks',
b'scheduler thread start',
@@ -87,16 +88,20 @@ class InitTest(BitcoinTestFramework):
]
if self.is_wallet_compiled():
lines_to_terminate_after.append(b'Verifying wallet')
lines_to_terminate_after.append(reindex_log_line)
for terminate_line in lines_to_terminate_after:
self.log.info(f"Starting node and will terminate after line {terminate_line}")
with node.busy_wait_for_debug_log([terminate_line]):
extra_args = [*ALL_INDEX_ARGS]
if terminate_line == reindex_log_line:
extra_args += ['-reindex']
if platform.system() == 'Windows':
# CREATE_NEW_PROCESS_GROUP is required in order to be able
# to terminate the child without terminating the test.
node.start(extra_args=ALL_INDEX_ARGS, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
node.start(extra_args=extra_args, creationflags=subprocess.CREATE_NEW_PROCESS_GROUP)
else:
node.start(extra_args=ALL_INDEX_ARGS)
node.start(extra_args=extra_args)
self.log.debug("Terminating node after terminate line was found")
sigterm_node()