elements/test/functional/feature_filelock.py
merge-script 8614ff46c9 Merge bitcoin/bitcoin#30435: init: change shutdown order of load block thread and scheduler
5fd4836019 init: change shutdown order of load block thread and scheduler (Martin Zumsande)

Pull request description:

  This avoids situations during a reindex, in which the shutdown doesn't finish since `LimitValidationInterfaceQueue()` is called by the load block thread when the scheduler is already stopped, in which case it would block indefinitely. This can lead to intermittent failures in `feature_reindex.py` (#30424), which I could locally reproduce with
  ```diff
  diff --git a/src/validation.cpp b/src/validation.cpp
  index 74f0e4975c..be1706fdaf 100644
  --- a/src/validation.cpp
  +++ b/src/validation.cpp
  @@ -3446,6 +3446,7 @@ static void LimitValidationInterfaceQueue(ValidationSignals& signals) LOCKS_EXCL
       AssertLockNotHeld(cs_main);

       if (signals.CallbacksPending() > 10) {
  +        std::this_thread::sleep_for(std::chrono::milliseconds(50));
           signals.SyncWithValidationInterfaceQueue();
       }
   }
  ```
  It has also been reported by users running `reindex-chainstate` (#23234).

  I thought for a bit about potential downsides of changing this order, but couldn't find any.

  Fixes #30424
  Fixes #23234

ACKs for top commit:
  maflcko:
    review ACK 5fd4836019
  hebasto:
    re-ACK 5fd4836019.
  tdb3:
    ACK 5fd4836019
  BrandonOdiwuor:
    Code Review ACK 5fd4836019

Tree-SHA512: 3b8894e99551c5d4392b55eaa718eee05841a7287aeef2978699e1d633d5234399fa2f5a3e71eac1508d97845906bd33e0e63e5351855139e7be04c421359b36
2025-06-24 10:25:22 +01:00

50 lines
2.6 KiB
Python
Executable file

#!/usr/bin/env python3
# Copyright (c) 2018-2021 The Bitcoin Core developers
# Distributed under the MIT software license, see the accompanying
# file COPYING or http://www.opensource.org/licenses/mit-license.php.
"""Check that it's not possible to start a second bitcoind instance using the same datadir or wallet."""
import os
import random
import string
from test_framework.test_framework import BitcoinTestFramework
from test_framework.test_node import ErrorMatch
class FilelockTest(BitcoinTestFramework):
def set_test_params(self):
self.setup_clean_chain = True
self.num_nodes = 2
def setup_network(self):
self.add_nodes(self.num_nodes, extra_args=None)
self.nodes[0].start()
self.nodes[0].wait_for_rpc_connection()
def run_test(self):
datadir = os.path.join(self.nodes[0].datadir, self.chain)
self.log.info(f"Using datadir {datadir}")
self.log.info("Check that we can't start a second bitcoind instance using the same datadir")
expected_msg = f"Error: Cannot obtain a lock on data directory {datadir}. {self.config['environment']['PACKAGE_NAME']} is probably already running."
self.nodes[1].assert_start_raises_init_error(extra_args=[f'-datadir={self.nodes[0].datadir}', '-noserver'], expected_msg=expected_msg)
cookie_file = datadir + "/.cookie"
assert os.path.isfile(cookie_file) # should not be deleted during the second bitcoind instance shutdown
if self.is_wallet_compiled():
def check_wallet_filelock(descriptors):
wallet_name = ''.join([random.choice(string.ascii_lowercase) for _ in range(6)])
self.nodes[0].createwallet(wallet_name=wallet_name, descriptors=descriptors)
wallet_dir = os.path.join(datadir, 'wallets')
self.log.info("Check that we can't start a second bitcoind instance using the same wallet")
if descriptors:
expected_msg = f"Error: SQLiteDatabase: Unable to obtain an exclusive lock on the database, is it being used by another instance of {self.config['environment']['PACKAGE_NAME']}?"
else:
expected_msg = "Error: Error initializing wallet database environment"
self.nodes[1].assert_start_raises_init_error(extra_args=[f'-walletdir={wallet_dir}', f'-wallet={wallet_name}', '-noserver'], expected_msg=expected_msg, match=ErrorMatch.PARTIAL_REGEX)
if self.is_bdb_compiled():
check_wallet_filelock(False)
if self.is_sqlite_compiled():
check_wallet_filelock(True)
if __name__ == '__main__':
FilelockTest().main()