validationinterface_tests.cpp raw
1 // Copyright (c) 2020 The Limenka 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/limenka/limenka/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()
105