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