/ src / test / validationinterface_tests.cpp
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()