Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

backport: bitcoin#18742, #28150 #5726

Merged
merged 2 commits into from
Nov 26, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 6 additions & 6 deletions src/rpc/mining.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -955,7 +955,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
return result;
}

class submitblock_StateCatcher : public CValidationInterface
class submitblock_StateCatcher final : public CValidationInterface
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Should this one be updated also (missing final)?

src/dsnotificationinterface.h:class CDSNotificationInterface : public CValidationInterface

{
public:
uint256 hash;
Expand Down Expand Up @@ -1016,17 +1016,17 @@ static UniValue submitblock(const JSONRPCRequest& request)
}

bool new_block;
submitblock_StateCatcher sc(block.GetHash());
RegisterValidationInterface(&sc);
auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterSharedValidationInterface(sc);
bool accepted = chainman.ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block);
UnregisterValidationInterface(&sc);
UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) {
return "duplicate";
}
if (!sc.found) {
if (!sc->found) {
return "inconclusive";
}
return BIP22ValidationResult(sc.state);
return BIP22ValidationResult(sc->state);
}

static UniValue submitheader(const JSONRPCRequest& request)
Expand Down
14 changes: 6 additions & 8 deletions src/test/validation_block_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static const std::vector<unsigned char> V_OP_TRUE{OP_TRUE};

BOOST_FIXTURE_TEST_SUITE(validation_block_tests, MinerTestingSetup)

struct TestSubscriber : public CValidationInterface {
struct TestSubscriber final : public CValidationInterface {
uint256 m_expected_tip;

explicit TestSubscriber(uint256 tip) : m_expected_tip(tip) {}
Expand Down Expand Up @@ -179,8 +179,8 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
LOCK(cs_main);
initial_tip = ::ChainActive().Tip();
}
TestSubscriber sub(initial_tip->GetBlockHash());
RegisterValidationInterface(&sub);
auto sub = std::make_shared<TestSubscriber>(initial_tip->GetBlockHash());
RegisterSharedValidationInterface(sub);

// create a bunch of threads that repeatedly process a block generated above at random
// this will create parallelism and randomness inside validation - the ValidationInterface
Expand Down Expand Up @@ -208,14 +208,12 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
for (auto& t : threads) {
t.join();
}
while (GetMainSignals().CallbacksPending() > 0) {
UninterruptibleSleep(std::chrono::milliseconds{100});
}
SyncWithValidationInterfaceQueue();

UnregisterValidationInterface(&sub);
UnregisterSharedValidationInterface(sub);

LOCK(cs_main);
BOOST_CHECK_EQUAL(sub.m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
BOOST_CHECK_EQUAL(sub->m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
}

/**
Expand Down
36 changes: 35 additions & 1 deletion src/test/validationinterface_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,41 @@
#include <util/check.h>
#include <validationinterface.h>

BOOST_FIXTURE_TEST_SUITE(validationinterface_tests, TestingSetup)
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;
const BlockValidationState state_dummy;
while (generate) {
GetMainSignals().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>();
RegisterSharedValidationInterface(sub);
UnregisterSharedValidationInterface(sub);
}
// tell the other thread we are done
generate = false;
}};

gen.join();
sub.join();
BOOST_CHECK(!generate);
}

class TestInterface : public CValidationInterface
{
Expand Down
35 changes: 22 additions & 13 deletions src/validationinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -94,22 +94,26 @@ struct MainSignalsInstance {

static CMainSignals g_signals;

void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) {
void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler)
{
assert(!m_internals);
m_internals.reset(new MainSignalsInstance(&scheduler));
}

void CMainSignals::UnregisterBackgroundSignalScheduler() {
void CMainSignals::UnregisterBackgroundSignalScheduler()
{
m_internals.reset(nullptr);
}

void CMainSignals::FlushBackgroundCallbacks() {
void CMainSignals::FlushBackgroundCallbacks()
{
if (m_internals) {
m_internals->m_schedulerClient.EmptyQueue();
}
}

size_t CMainSignals::CallbacksPending() {
size_t CMainSignals::CallbacksPending()
{
if (!m_internals) return 0;
return m_internals->m_schedulerClient.CallbacksPending();
}
Expand All @@ -119,10 +123,11 @@ CMainSignals& GetMainSignals()
return g_signals;
}

void RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> pwalletIn) {
// Each connection captures pwalletIn to ensure that each callback is
// executed before pwalletIn is destroyed. For more details see #18338.
g_signals.m_internals->Register(std::move(pwalletIn));
void RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
{
// Each connection captures the shared_ptr to ensure that each callback is
// executed before the subscriber is destroyed. For more details see #18338.
g_signals.m_internals->Register(std::move(callbacks));
}

void RegisterValidationInterface(CValidationInterface* callbacks)
Expand All @@ -137,24 +142,28 @@ void UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> c
UnregisterValidationInterface(callbacks.get());
}

void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
void UnregisterValidationInterface(CValidationInterface* callbacks)
{
if (g_signals.m_internals) {
g_signals.m_internals->Unregister(pwalletIn);
g_signals.m_internals->Unregister(callbacks);
}
}

void UnregisterAllValidationInterfaces() {
void UnregisterAllValidationInterfaces()
{
if (!g_signals.m_internals) {
return;
}
g_signals.m_internals->Clear();
}

void CallFunctionInValidationInterfaceQueue(std::function<void ()> func) {
void CallFunctionInValidationInterfaceQueue(std::function<void()> func)
{
g_signals.m_internals->m_schedulerClient.AddToProcessQueue(std::move(func));
}

void SyncWithValidationInterfaceQueue() {
void SyncWithValidationInterfaceQueue()
{
AssertLockNotHeld(cs_main);
// Block until the validation queue drains
std::promise<void> promise;
Expand Down
14 changes: 7 additions & 7 deletions src/validationinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,20 +33,20 @@ namespace llmq {
class CRecoveredSig;
} // namespace llmq

// These functions dispatch to one or all registered wallets

/** Register a wallet to receive updates from core */
void RegisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister a wallet from core */
void UnregisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister all wallets from core */
/** Register subscriber */
void RegisterValidationInterface(CValidationInterface* callbacks);
/** Unregister subscriber. DEPRECATED. This is not safe to use when the RPC server or main message handler thread is running. */
void UnregisterValidationInterface(CValidationInterface* callbacks);
/** Unregister all subscribers */
void UnregisterAllValidationInterfaces();

// Alternate registration functions that release a shared_ptr after the last
// notification is sent. These are useful for race-free cleanup, since
// unregistration is nonblocking and can return before the last notification is
// processed.
/** Register subscriber */
void RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks);
/** Unregister subscriber */
void UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks);

/**
Expand Down
Loading