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