Merge pull request #5726 from UdjinM6/bp_18742_28150

backport: bitcoin#18742, #28150
This commit is contained in:
PastaPastaPasta 2023-11-26 15:10:38 -06:00 committed by Odysseas Gabrielides
parent 330c5f9451
commit 4660170eae
5 changed files with 76 additions and 35 deletions

View File

@ -955,7 +955,7 @@ static UniValue getblocktemplate(const JSONRPCRequest& request)
return result; return result;
} }
class submitblock_StateCatcher : public CValidationInterface class submitblock_StateCatcher final : public CValidationInterface
{ {
public: public:
uint256 hash; uint256 hash;
@ -1016,17 +1016,17 @@ static UniValue submitblock(const JSONRPCRequest& request)
} }
bool new_block; bool new_block;
submitblock_StateCatcher sc(block.GetHash()); auto sc = std::make_shared<submitblock_StateCatcher>(block.GetHash());
RegisterValidationInterface(&sc); RegisterSharedValidationInterface(sc);
bool accepted = chainman.ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block); bool accepted = chainman.ProcessNewBlock(Params(), blockptr, /* fForceProcessing */ true, /* fNewBlock */ &new_block);
UnregisterValidationInterface(&sc); UnregisterSharedValidationInterface(sc);
if (!new_block && accepted) { if (!new_block && accepted) {
return "duplicate"; return "duplicate";
} }
if (!sc.found) { if (!sc->found) {
return "inconclusive"; return "inconclusive";
} }
return BIP22ValidationResult(sc.state); return BIP22ValidationResult(sc->state);
} }
static UniValue submitheader(const JSONRPCRequest& request) static UniValue submitheader(const JSONRPCRequest& request)

View File

@ -40,7 +40,7 @@ static const std::vector<unsigned char> V_OP_TRUE{OP_TRUE};
BOOST_FIXTURE_TEST_SUITE(validation_block_tests, MinerTestingSetup) BOOST_FIXTURE_TEST_SUITE(validation_block_tests, MinerTestingSetup)
struct TestSubscriber : public CValidationInterface { struct TestSubscriber final : public CValidationInterface {
uint256 m_expected_tip; uint256 m_expected_tip;
explicit TestSubscriber(uint256 tip) : m_expected_tip(tip) {} explicit TestSubscriber(uint256 tip) : m_expected_tip(tip) {}
@ -179,8 +179,8 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
LOCK(cs_main); LOCK(cs_main);
initial_tip = ::ChainActive().Tip(); initial_tip = ::ChainActive().Tip();
} }
TestSubscriber sub(initial_tip->GetBlockHash()); auto sub = std::make_shared<TestSubscriber>(initial_tip->GetBlockHash());
RegisterValidationInterface(&sub); RegisterSharedValidationInterface(sub);
// create a bunch of threads that repeatedly process a block generated above at random // create a bunch of threads that repeatedly process a block generated above at random
// this will create parallelism and randomness inside validation - the ValidationInterface // this will create parallelism and randomness inside validation - the ValidationInterface
@ -208,14 +208,12 @@ BOOST_AUTO_TEST_CASE(processnewblock_signals_ordering)
for (auto& t : threads) { for (auto& t : threads) {
t.join(); t.join();
} }
while (GetMainSignals().CallbacksPending() > 0) { SyncWithValidationInterfaceQueue();
UninterruptibleSleep(std::chrono::milliseconds{100});
}
UnregisterValidationInterface(&sub); UnregisterSharedValidationInterface(sub);
LOCK(cs_main); LOCK(cs_main);
BOOST_CHECK_EQUAL(sub.m_expected_tip, ::ChainActive().Tip()->GetBlockHash()); BOOST_CHECK_EQUAL(sub->m_expected_tip, ::ChainActive().Tip()->GetBlockHash());
} }
/** /**

View File

@ -10,7 +10,41 @@
#include <util/check.h> #include <util/check.h>
#include <validationinterface.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 class TestInterface : public CValidationInterface
{ {

View File

@ -94,22 +94,26 @@ public:
static CMainSignals g_signals; static CMainSignals g_signals;
void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) { void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler)
{
assert(!m_internals); assert(!m_internals);
m_internals.reset(new MainSignalsInstance(&scheduler)); m_internals.reset(new MainSignalsInstance(&scheduler));
} }
void CMainSignals::UnregisterBackgroundSignalScheduler() { void CMainSignals::UnregisterBackgroundSignalScheduler()
{
m_internals.reset(nullptr); m_internals.reset(nullptr);
} }
void CMainSignals::FlushBackgroundCallbacks() { void CMainSignals::FlushBackgroundCallbacks()
{
if (m_internals) { if (m_internals) {
m_internals->m_schedulerClient.EmptyQueue(); m_internals->m_schedulerClient.EmptyQueue();
} }
} }
size_t CMainSignals::CallbacksPending() { size_t CMainSignals::CallbacksPending()
{
if (!m_internals) return 0; if (!m_internals) return 0;
return m_internals->m_schedulerClient.CallbacksPending(); return m_internals->m_schedulerClient.CallbacksPending();
} }
@ -119,10 +123,11 @@ CMainSignals& GetMainSignals()
return g_signals; return g_signals;
} }
void RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> pwalletIn) { void RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks)
// Each connection captures pwalletIn to ensure that each callback is {
// executed before pwalletIn is destroyed. For more details see #18338. // Each connection captures the shared_ptr to ensure that each callback is
g_signals.m_internals->Register(std::move(pwalletIn)); // executed before the subscriber is destroyed. For more details see #18338.
g_signals.m_internals->Register(std::move(callbacks));
} }
void RegisterValidationInterface(CValidationInterface* callbacks) void RegisterValidationInterface(CValidationInterface* callbacks)
@ -137,24 +142,28 @@ void UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> c
UnregisterValidationInterface(callbacks.get()); UnregisterValidationInterface(callbacks.get());
} }
void UnregisterValidationInterface(CValidationInterface* pwalletIn) { void UnregisterValidationInterface(CValidationInterface* callbacks)
{
if (g_signals.m_internals) { 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) { if (!g_signals.m_internals) {
return; return;
} }
g_signals.m_internals->Clear(); 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)); g_signals.m_internals->m_schedulerClient.AddToProcessQueue(std::move(func));
} }
void SyncWithValidationInterfaceQueue() { void SyncWithValidationInterfaceQueue()
{
AssertLockNotHeld(cs_main); AssertLockNotHeld(cs_main);
// Block until the validation queue drains // Block until the validation queue drains
std::promise<void> promise; std::promise<void> promise;

View File

@ -33,20 +33,20 @@ namespace llmq {
class CRecoveredSig; class CRecoveredSig;
} // namespace llmq } // namespace llmq
// These functions dispatch to one or all registered wallets /** Register subscriber */
void RegisterValidationInterface(CValidationInterface* callbacks);
/** Register a wallet to receive updates from core */ /** Unregister subscriber. DEPRECATED. This is not safe to use when the RPC server or main message handler thread is running. */
void RegisterValidationInterface(CValidationInterface* pwalletIn); void UnregisterValidationInterface(CValidationInterface* callbacks);
/** Unregister a wallet from core */ /** Unregister all subscribers */
void UnregisterValidationInterface(CValidationInterface* pwalletIn);
/** Unregister all wallets from core */
void UnregisterAllValidationInterfaces(); void UnregisterAllValidationInterfaces();
// Alternate registration functions that release a shared_ptr after the last // Alternate registration functions that release a shared_ptr after the last
// notification is sent. These are useful for race-free cleanup, since // notification is sent. These are useful for race-free cleanup, since
// unregistration is nonblocking and can return before the last notification is // unregistration is nonblocking and can return before the last notification is
// processed. // processed.
/** Register subscriber */
void RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks); void RegisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks);
/** Unregister subscriber */
void UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks); void UnregisterSharedValidationInterface(std::shared_ptr<CValidationInterface> callbacks);
/** /**