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