validationinterface_tests.cpp
1 // Copyright (c) 2020-present 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 <validationinterface.h> 12 13 #include <atomic> 14 #include <memory> 15 16 BOOST_FIXTURE_TEST_SUITE(validationinterface_tests, ChainTestingSetup) 17 18 struct TestSubscriberNoop final : public CValidationInterface { 19 void BlockChecked(const std::shared_ptr<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 BlockValidationState state_dummy; 29 while (generate) { 30 m_node.validation_signals->BlockChecked(std::make_shared<const CBlock>(), state_dummy); 31 } 32 }}; 33 34 // Start thread to consume notifications 35 std::thread sub{[&] { 36 // keep going for about 1 sec, which is 250k iterations 37 for (int i = 0; i < 250000; i++) { 38 auto sub = std::make_shared<TestSubscriberNoop>(); 39 m_node.validation_signals->RegisterSharedValidationInterface(sub); 40 m_node.validation_signals->UnregisterSharedValidationInterface(sub); 41 } 42 // tell the other thread we are done 43 generate = false; 44 }}; 45 46 gen.join(); 47 sub.join(); 48 BOOST_CHECK(!generate); 49 } 50 51 class TestInterface : public CValidationInterface 52 { 53 public: 54 TestInterface(ValidationSignals& signals, std::function<void()> on_call = nullptr, std::function<void()> on_destroy = nullptr) 55 : m_on_call(std::move(on_call)), m_on_destroy(std::move(on_destroy)), m_signals{signals} 56 { 57 } 58 virtual ~TestInterface() 59 { 60 if (m_on_destroy) m_on_destroy(); 61 } 62 void BlockChecked(const std::shared_ptr<const CBlock>& block, const BlockValidationState& state) override 63 { 64 if (m_on_call) m_on_call(); 65 } 66 void Call() 67 { 68 BlockValidationState state; 69 m_signals.BlockChecked(std::make_shared<const CBlock>(), state); 70 } 71 std::function<void()> m_on_call; 72 std::function<void()> m_on_destroy; 73 ValidationSignals& m_signals; 74 }; 75 76 // Regression test to ensure UnregisterAllValidationInterfaces calls don't 77 // destroy a validation interface while it is being called. Bug: 78 // https://github.com/bitcoin/bitcoin/pull/18551 79 BOOST_AUTO_TEST_CASE(unregister_all_during_call) 80 { 81 bool destroyed = false; 82 auto shared{std::make_shared<TestInterface>( 83 *m_node.validation_signals, 84 [&] { 85 // First call should decrements reference count 2 -> 1 86 m_node.validation_signals->UnregisterAllValidationInterfaces(); 87 BOOST_CHECK(!destroyed); 88 // Second call should not decrement reference count 1 -> 0 89 m_node.validation_signals->UnregisterAllValidationInterfaces(); 90 BOOST_CHECK(!destroyed); 91 }, 92 [&] { destroyed = true; })}; 93 m_node.validation_signals->RegisterSharedValidationInterface(shared); 94 BOOST_CHECK(shared.use_count() == 2); 95 shared->Call(); 96 BOOST_CHECK(shared.use_count() == 1); 97 BOOST_CHECK(!destroyed); 98 shared.reset(); 99 BOOST_CHECK(destroyed); 100 } 101 102 BOOST_AUTO_TEST_SUITE_END()