validationinterface_tests.cpp raw

   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()
 103