validationinterface_tests.cpp
1 // Copyright (c) 2020 The Bitcoin Core developers 2 // Distributed under the MIT software license, see the accompanying 3 // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 5 #include <boost/test/unit_test.hpp> 6 #include <consensus/validation.h> 7 #include <primitives/block.h> 8 #include <scheduler.h> 9 #include <test/util/setup_common.h> 10 #include <util/check.h> 11 #include <kernel/chain.h> 12 #include <validationinterface.h> 13 14 #include <atomic> 15 16 BOOST_FIXTURE_TEST_SUITE(validationinterface_tests, ChainTestingSetup) 17 18 struct TestSubscriberNoop final : public CValidationInterface { 19 void BlockChecked(const CBlock&, const BlockValidationState&) override {} 20 }; 21 22 BOOST_AUTO_TEST_CASE(unregister_validation_interface_race) 23 { 24 std::atomic<bool> generate{true}; 25 26 // Start thread to generate notifications 27 std::thread gen{[&] { 28 const CBlock block_dummy; 29 BlockValidationState state_dummy; 30 while (generate) { 31 m_node.validation_signals->BlockChecked(block_dummy, state_dummy); 32 } 33 }}; 34 35 // Start thread to consume notifications 36 std::thread sub{[&] { 37 // keep going for about 1 sec, which is 250k iterations 38 for (int i = 0; i < 250000; i++) { 39 auto sub = std::make_shared<TestSubscriberNoop>(); 40 m_node.validation_signals->RegisterSharedValidationInterface(sub); 41 m_node.validation_signals->UnregisterSharedValidationInterface(sub); 42 } 43 // tell the other thread we are done 44 generate = false; 45 }}; 46 47 gen.join(); 48 sub.join(); 49 BOOST_CHECK(!generate); 50 } 51 52 class TestInterface : public CValidationInterface 53 { 54 public: 55 TestInterface(ValidationSignals& signals, std::function<void()> on_call = nullptr, std::function<void()> on_destroy = nullptr) 56 : m_on_call(std::move(on_call)), m_on_destroy(std::move(on_destroy)), m_signals{signals} 57 { 58 } 59 virtual ~TestInterface() 60 { 61 if (m_on_destroy) m_on_destroy(); 62 } 63 void BlockChecked(const CBlock& block, const BlockValidationState& state) override 64 { 65 if (m_on_call) m_on_call(); 66 } 67 void Call() 68 { 69 CBlock block; 70 BlockValidationState state; 71 m_signals.BlockChecked(block, state); 72 } 73 std::function<void()> m_on_call; 74 std::function<void()> m_on_destroy; 75 ValidationSignals& m_signals; 76 }; 77 78 // Regression test to ensure UnregisterAllValidationInterfaces calls don't 79 // destroy a validation interface while it is being called. Bug: 80 // https://github.com/bitcoin/bitcoin/pull/18551 81 BOOST_AUTO_TEST_CASE(unregister_all_during_call) 82 { 83 bool destroyed = false; 84 auto shared{std::make_shared<TestInterface>( 85 *m_node.validation_signals, 86 [&] { 87 // First call should decrements reference count 2 -> 1 88 m_node.validation_signals->UnregisterAllValidationInterfaces(); 89 BOOST_CHECK(!destroyed); 90 // Second call should not decrement reference count 1 -> 0 91 m_node.validation_signals->UnregisterAllValidationInterfaces(); 92 BOOST_CHECK(!destroyed); 93 }, 94 [&] { destroyed = true; })}; 95 m_node.validation_signals->RegisterSharedValidationInterface(shared); 96 BOOST_CHECK(shared.use_count() == 2); 97 shared->Call(); 98 BOOST_CHECK(shared.use_count() == 1); 99 BOOST_CHECK(!destroyed); 100 shared.reset(); 101 BOOST_CHECK(destroyed); 102 } 103 104 BOOST_AUTO_TEST_SUITE_END()