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