mirror of
https://github.com/bitcoin/bitcoin.git
synced 2025-02-13 11:25:02 -05:00
![TheCharlatan](/assets/img/avatar_default.png)
-BEGIN VERIFY SCRIPT- s() { git grep -l "$1" src | xargs sed -i "s/$1/$2/g"; } s 'CMainSignals' 'ValidationSignals' s 'MainSignalsImpl' 'ValidationSignalsImpl' -END VERIFY SCRIPT-
104 lines
3.3 KiB
C++
104 lines
3.3 KiB
C++
// Copyright (c) 2020 The Bitcoin Core developers
|
|
// Distributed under the MIT software license, see the accompanying
|
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
|
|
|
#include <boost/test/unit_test.hpp>
|
|
#include <consensus/validation.h>
|
|
#include <primitives/block.h>
|
|
#include <scheduler.h>
|
|
#include <test/util/setup_common.h>
|
|
#include <util/check.h>
|
|
#include <kernel/chain.h>
|
|
#include <validationinterface.h>
|
|
|
|
#include <atomic>
|
|
|
|
BOOST_FIXTURE_TEST_SUITE(validationinterface_tests, ChainTestingSetup)
|
|
|
|
struct TestSubscriberNoop final : public CValidationInterface {
|
|
void BlockChecked(const CBlock&, const BlockValidationState&) override {}
|
|
};
|
|
|
|
BOOST_AUTO_TEST_CASE(unregister_validation_interface_race)
|
|
{
|
|
std::atomic<bool> generate{true};
|
|
|
|
// Start thread to generate notifications
|
|
std::thread gen{[&] {
|
|
const CBlock block_dummy;
|
|
BlockValidationState state_dummy;
|
|
while (generate) {
|
|
m_node.validation_signals->BlockChecked(block_dummy, state_dummy);
|
|
}
|
|
}};
|
|
|
|
// Start thread to consume notifications
|
|
std::thread sub{[&] {
|
|
// keep going for about 1 sec, which is 250k iterations
|
|
for (int i = 0; i < 250000; i++) {
|
|
auto sub = std::make_shared<TestSubscriberNoop>();
|
|
m_node.validation_signals->RegisterSharedValidationInterface(sub);
|
|
m_node.validation_signals->UnregisterSharedValidationInterface(sub);
|
|
}
|
|
// tell the other thread we are done
|
|
generate = false;
|
|
}};
|
|
|
|
gen.join();
|
|
sub.join();
|
|
BOOST_CHECK(!generate);
|
|
}
|
|
|
|
class TestInterface : public CValidationInterface
|
|
{
|
|
public:
|
|
TestInterface(ValidationSignals& signals, std::function<void()> on_call = nullptr, std::function<void()> on_destroy = nullptr)
|
|
: m_on_call(std::move(on_call)), m_on_destroy(std::move(on_destroy)), m_signals{signals}
|
|
{
|
|
}
|
|
virtual ~TestInterface()
|
|
{
|
|
if (m_on_destroy) m_on_destroy();
|
|
}
|
|
void BlockChecked(const CBlock& block, const BlockValidationState& state) override
|
|
{
|
|
if (m_on_call) m_on_call();
|
|
}
|
|
void Call()
|
|
{
|
|
CBlock block;
|
|
BlockValidationState state;
|
|
m_signals.BlockChecked(block, state);
|
|
}
|
|
std::function<void()> m_on_call;
|
|
std::function<void()> m_on_destroy;
|
|
ValidationSignals& m_signals;
|
|
};
|
|
|
|
// Regression test to ensure UnregisterAllValidationInterfaces calls don't
|
|
// destroy a validation interface while it is being called. Bug:
|
|
// https://github.com/bitcoin/bitcoin/pull/18551
|
|
BOOST_AUTO_TEST_CASE(unregister_all_during_call)
|
|
{
|
|
bool destroyed = false;
|
|
auto shared{std::make_shared<TestInterface>(
|
|
*m_node.validation_signals,
|
|
[&] {
|
|
// First call should decrements reference count 2 -> 1
|
|
m_node.validation_signals->UnregisterAllValidationInterfaces();
|
|
BOOST_CHECK(!destroyed);
|
|
// Second call should not decrement reference count 1 -> 0
|
|
m_node.validation_signals->UnregisterAllValidationInterfaces();
|
|
BOOST_CHECK(!destroyed);
|
|
},
|
|
[&] { destroyed = true; })};
|
|
m_node.validation_signals->RegisterSharedValidationInterface(shared);
|
|
BOOST_CHECK(shared.use_count() == 2);
|
|
shared->Call();
|
|
BOOST_CHECK(shared.use_count() == 1);
|
|
BOOST_CHECK(!destroyed);
|
|
shared.reset();
|
|
BOOST_CHECK(destroyed);
|
|
}
|
|
|
|
BOOST_AUTO_TEST_SUITE_END()
|