Merge #10179: Give CValidationInterface Support for calling notifications on the CScheduler Thread
1f668b6
Expose if CScheduler is being serviced, assert its not in EmptyQueue (Matt Corallo)3192975
Flush CValidationInterface callbacks prior to destruction (Matt Corallo)08096bb
Support more than one CScheduler thread for serial clients (Matt Corallo)2fbf2db
Add default arg to CScheduler to schedule() a callback now (Matt Corallo)cda1429
Give CMainSignals a reference to the global scheduler (Matt Corallo)3a19fed
Make ValidationInterface signals-type-agnostic (Matt Corallo)ff6a834
Use TestingSetup to DRY qt rpcnestedtests (Matt Corallo) Tree-SHA512: fab91e34e30b080ed4d0a6d8c1214910e383c45440676e37be61d0bde6ae98d61e8903d22b846e95ba4e73a6ce788798350266feba246d8a2ab357e8523e4ac5
This commit is contained in:
commit
21ed30a314
8 changed files with 274 additions and 73 deletions
16
src/init.cpp
16
src/init.cpp
|
@ -215,6 +215,19 @@ void Shutdown()
|
||||||
fFeeEstimatesInitialized = false;
|
fFeeEstimatesInitialized = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FlushStateToDisk generates a SetBestChain callback, which we should avoid missing
|
||||||
|
FlushStateToDisk();
|
||||||
|
|
||||||
|
// After there are no more peers/RPC left to give us new data which may generate
|
||||||
|
// CValidationInterface callbacks, flush them...
|
||||||
|
GetMainSignals().FlushBackgroundCallbacks();
|
||||||
|
|
||||||
|
// Any future callbacks will be dropped. This should absolutely be safe - if
|
||||||
|
// missing a callback results in an unrecoverable situation, unclean shutdown
|
||||||
|
// would too. The only reason to do the above flushes is to let the wallet catch
|
||||||
|
// up with our current chain to avoid any strange pruning edge cases and make
|
||||||
|
// next startup faster by avoiding rescan.
|
||||||
|
|
||||||
{
|
{
|
||||||
LOCK(cs_main);
|
LOCK(cs_main);
|
||||||
if (pcoinsTip != NULL) {
|
if (pcoinsTip != NULL) {
|
||||||
|
@ -251,6 +264,7 @@ void Shutdown()
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
UnregisterAllValidationInterfaces();
|
UnregisterAllValidationInterfaces();
|
||||||
|
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
||||||
#ifdef ENABLE_WALLET
|
#ifdef ENABLE_WALLET
|
||||||
for (CWalletRef pwallet : vpwallets) {
|
for (CWalletRef pwallet : vpwallets) {
|
||||||
delete pwallet;
|
delete pwallet;
|
||||||
|
@ -1203,6 +1217,8 @@ bool AppInitMain(boost::thread_group& threadGroup, CScheduler& scheduler)
|
||||||
CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler);
|
CScheduler::Function serviceLoop = boost::bind(&CScheduler::serviceQueue, &scheduler);
|
||||||
threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
|
threadGroup.create_thread(boost::bind(&TraceThread<CScheduler::Function>, "scheduler", serviceLoop));
|
||||||
|
|
||||||
|
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
|
||||||
|
|
||||||
/* Start the RPC server already. It will be started in "warmup" mode
|
/* Start the RPC server already. It will be started in "warmup" mode
|
||||||
* and not really process calls already (but it will signify connections
|
* and not really process calls already (but it will signify connections
|
||||||
* that the server is there and will be ready later). Warmup mode will
|
* that the server is there and will be ready later). Warmup mode will
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
#include "rpc/server.h"
|
#include "rpc/server.h"
|
||||||
#include "rpcconsole.h"
|
#include "rpcconsole.h"
|
||||||
#include "test/testutil.h"
|
#include "test/testutil.h"
|
||||||
|
#include "test/test_bitcoin.h"
|
||||||
#include "univalue.h"
|
#include "univalue.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
|
|
||||||
|
@ -35,8 +36,6 @@ void RPCNestedTests::rpcNestedTests()
|
||||||
{
|
{
|
||||||
// do some test setup
|
// do some test setup
|
||||||
// could be moved to a more generic place when we add more tests on QT level
|
// could be moved to a more generic place when we add more tests on QT level
|
||||||
const CChainParams& chainparams = Params();
|
|
||||||
RegisterAllCoreRPCCommands(tableRPC);
|
|
||||||
tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
|
tableRPC.appendCommand("rpcNestedTest", &vRPCCommands[0]);
|
||||||
ClearDatadirCache();
|
ClearDatadirCache();
|
||||||
std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000)));
|
std::string path = QDir::tempPath().toStdString() + "/" + strprintf("test_bitcoin_qt_%lu_%i", (unsigned long)GetTime(), (int)(GetRand(100000)));
|
||||||
|
@ -44,15 +43,8 @@ void RPCNestedTests::rpcNestedTests()
|
||||||
dir.mkpath(".");
|
dir.mkpath(".");
|
||||||
ForceSetArg("-datadir", path);
|
ForceSetArg("-datadir", path);
|
||||||
//mempool.setSanityCheck(1.0);
|
//mempool.setSanityCheck(1.0);
|
||||||
pblocktree = new CBlockTreeDB(1 << 20, true);
|
|
||||||
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
TestingSetup test;
|
||||||
pcoinsTip = new CCoinsViewCache(pcoinsdbview);
|
|
||||||
InitBlockIndex(chainparams);
|
|
||||||
{
|
|
||||||
CValidationState state;
|
|
||||||
bool ok = ActivateBestChain(state, chainparams);
|
|
||||||
QVERIFY(ok);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetRPCWarmupFinished();
|
SetRPCWarmupFinished();
|
||||||
|
|
||||||
|
@ -145,13 +137,5 @@ void RPCNestedTests::rpcNestedTests()
|
||||||
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using ,
|
QVERIFY_EXCEPTION_THROWN(RPCConsole::RPCExecuteCommandLine(result, "rpcNestedTest(abc,,)"), std::runtime_error); //don't tollerate empty arguments when using ,
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
UnloadBlockIndex();
|
|
||||||
delete pcoinsTip;
|
|
||||||
pcoinsTip = nullptr;
|
|
||||||
delete pcoinsdbview;
|
|
||||||
pcoinsdbview = nullptr;
|
|
||||||
delete pblocktree;
|
|
||||||
pblocktree = nullptr;
|
|
||||||
|
|
||||||
fs::remove_all(fs::path(path));
|
fs::remove_all(fs::path(path));
|
||||||
}
|
}
|
||||||
|
|
|
@ -139,3 +139,69 @@ size_t CScheduler::getQueueInfo(boost::chrono::system_clock::time_point &first,
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool CScheduler::AreThreadsServicingQueue() const {
|
||||||
|
return nThreadsServicingQueue;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void SingleThreadedSchedulerClient::MaybeScheduleProcessQueue() {
|
||||||
|
{
|
||||||
|
LOCK(m_cs_callbacks_pending);
|
||||||
|
// Try to avoid scheduling too many copies here, but if we
|
||||||
|
// accidentally have two ProcessQueue's scheduled at once its
|
||||||
|
// not a big deal.
|
||||||
|
if (m_are_callbacks_running) return;
|
||||||
|
if (m_callbacks_pending.empty()) return;
|
||||||
|
}
|
||||||
|
m_pscheduler->schedule(std::bind(&SingleThreadedSchedulerClient::ProcessQueue, this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleThreadedSchedulerClient::ProcessQueue() {
|
||||||
|
std::function<void (void)> callback;
|
||||||
|
{
|
||||||
|
LOCK(m_cs_callbacks_pending);
|
||||||
|
if (m_are_callbacks_running) return;
|
||||||
|
if (m_callbacks_pending.empty()) return;
|
||||||
|
m_are_callbacks_running = true;
|
||||||
|
|
||||||
|
callback = std::move(m_callbacks_pending.front());
|
||||||
|
m_callbacks_pending.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
// RAII the setting of fCallbacksRunning and calling MaybeScheduleProcessQueue
|
||||||
|
// to ensure both happen safely even if callback() throws.
|
||||||
|
struct RAIICallbacksRunning {
|
||||||
|
SingleThreadedSchedulerClient* instance;
|
||||||
|
RAIICallbacksRunning(SingleThreadedSchedulerClient* _instance) : instance(_instance) {}
|
||||||
|
~RAIICallbacksRunning() {
|
||||||
|
{
|
||||||
|
LOCK(instance->m_cs_callbacks_pending);
|
||||||
|
instance->m_are_callbacks_running = false;
|
||||||
|
}
|
||||||
|
instance->MaybeScheduleProcessQueue();
|
||||||
|
}
|
||||||
|
} raiicallbacksrunning(this);
|
||||||
|
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleThreadedSchedulerClient::AddToProcessQueue(std::function<void (void)> func) {
|
||||||
|
assert(m_pscheduler);
|
||||||
|
|
||||||
|
{
|
||||||
|
LOCK(m_cs_callbacks_pending);
|
||||||
|
m_callbacks_pending.emplace_back(std::move(func));
|
||||||
|
}
|
||||||
|
MaybeScheduleProcessQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SingleThreadedSchedulerClient::EmptyQueue() {
|
||||||
|
assert(!m_pscheduler->AreThreadsServicingQueue());
|
||||||
|
bool should_continue = true;
|
||||||
|
while (should_continue) {
|
||||||
|
ProcessQueue();
|
||||||
|
LOCK(m_cs_callbacks_pending);
|
||||||
|
should_continue = !m_callbacks_pending.empty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -14,6 +14,8 @@
|
||||||
#include <boost/thread.hpp>
|
#include <boost/thread.hpp>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
|
||||||
|
#include "sync.h"
|
||||||
|
|
||||||
//
|
//
|
||||||
// Simple class for background tasks that should be run
|
// Simple class for background tasks that should be run
|
||||||
// periodically or once "after a while"
|
// periodically or once "after a while"
|
||||||
|
@ -41,7 +43,7 @@ public:
|
||||||
typedef std::function<void(void)> Function;
|
typedef std::function<void(void)> Function;
|
||||||
|
|
||||||
// Call func at/after time t
|
// Call func at/after time t
|
||||||
void schedule(Function f, boost::chrono::system_clock::time_point t);
|
void schedule(Function f, boost::chrono::system_clock::time_point t=boost::chrono::system_clock::now());
|
||||||
|
|
||||||
// Convenience method: call f once deltaSeconds from now
|
// Convenience method: call f once deltaSeconds from now
|
||||||
void scheduleFromNow(Function f, int64_t deltaMilliSeconds);
|
void scheduleFromNow(Function f, int64_t deltaMilliSeconds);
|
||||||
|
@ -69,6 +71,9 @@ public:
|
||||||
size_t getQueueInfo(boost::chrono::system_clock::time_point &first,
|
size_t getQueueInfo(boost::chrono::system_clock::time_point &first,
|
||||||
boost::chrono::system_clock::time_point &last) const;
|
boost::chrono::system_clock::time_point &last) const;
|
||||||
|
|
||||||
|
// Returns true if there are threads actively running in serviceQueue()
|
||||||
|
bool AreThreadsServicingQueue() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::multimap<boost::chrono::system_clock::time_point, Function> taskQueue;
|
std::multimap<boost::chrono::system_clock::time_point, Function> taskQueue;
|
||||||
boost::condition_variable newTaskScheduled;
|
boost::condition_variable newTaskScheduled;
|
||||||
|
@ -79,4 +84,30 @@ private:
|
||||||
bool shouldStop() { return stopRequested || (stopWhenEmpty && taskQueue.empty()); }
|
bool shouldStop() { return stopRequested || (stopWhenEmpty && taskQueue.empty()); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class used by CScheduler clients which may schedule multiple jobs
|
||||||
|
* which are required to be run serially. Does not require such jobs
|
||||||
|
* to be executed on the same thread, but no two jobs will be executed
|
||||||
|
* at the same time.
|
||||||
|
*/
|
||||||
|
class SingleThreadedSchedulerClient {
|
||||||
|
private:
|
||||||
|
CScheduler *m_pscheduler;
|
||||||
|
|
||||||
|
CCriticalSection m_cs_callbacks_pending;
|
||||||
|
std::list<std::function<void (void)>> m_callbacks_pending;
|
||||||
|
bool m_are_callbacks_running = false;
|
||||||
|
|
||||||
|
void MaybeScheduleProcessQueue();
|
||||||
|
void ProcessQueue();
|
||||||
|
|
||||||
|
public:
|
||||||
|
SingleThreadedSchedulerClient(CScheduler *pschedulerIn) : m_pscheduler(pschedulerIn) {}
|
||||||
|
void AddToProcessQueue(std::function<void (void)> func);
|
||||||
|
|
||||||
|
// Processes all remaining queue members on the calling thread, blocking until queue is empty
|
||||||
|
// Must be called after the CScheduler has no remaining processing threads!
|
||||||
|
void EmptyQueue();
|
||||||
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -62,6 +62,12 @@ TestingSetup::TestingSetup(const std::string& chainName) : BasicTestingSetup(cha
|
||||||
pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000)));
|
pathTemp = GetTempPath() / strprintf("test_bitcoin_%lu_%i", (unsigned long)GetTime(), (int)(InsecureRandRange(100000)));
|
||||||
fs::create_directories(pathTemp);
|
fs::create_directories(pathTemp);
|
||||||
ForceSetArg("-datadir", pathTemp.string());
|
ForceSetArg("-datadir", pathTemp.string());
|
||||||
|
|
||||||
|
// Note that because we don't bother running a scheduler thread here,
|
||||||
|
// callbacks via CValidationInterface are unreliable, but that's OK,
|
||||||
|
// our unit tests aren't testing multiple parts of the code at once.
|
||||||
|
GetMainSignals().RegisterBackgroundSignalScheduler(scheduler);
|
||||||
|
|
||||||
mempool.setSanityCheck(1.0);
|
mempool.setSanityCheck(1.0);
|
||||||
pblocktree = new CBlockTreeDB(1 << 20, true);
|
pblocktree = new CBlockTreeDB(1 << 20, true);
|
||||||
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
pcoinsdbview = new CCoinsViewDB(1 << 23, true);
|
||||||
|
@ -88,6 +94,8 @@ TestingSetup::~TestingSetup()
|
||||||
UnregisterNodeSignals(GetNodeSignals());
|
UnregisterNodeSignals(GetNodeSignals());
|
||||||
threadGroup.interrupt_all();
|
threadGroup.interrupt_all();
|
||||||
threadGroup.join_all();
|
threadGroup.join_all();
|
||||||
|
GetMainSignals().FlushBackgroundCallbacks();
|
||||||
|
GetMainSignals().UnregisterBackgroundSignalScheduler();
|
||||||
UnloadBlockIndex();
|
UnloadBlockIndex();
|
||||||
delete pcoinsTip;
|
delete pcoinsTip;
|
||||||
delete pcoinsdbview;
|
delete pcoinsdbview;
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "key.h"
|
#include "key.h"
|
||||||
#include "pubkey.h"
|
#include "pubkey.h"
|
||||||
#include "random.h"
|
#include "random.h"
|
||||||
|
#include "scheduler.h"
|
||||||
#include "txdb.h"
|
#include "txdb.h"
|
||||||
#include "txmempool.h"
|
#include "txmempool.h"
|
||||||
|
|
||||||
|
@ -53,6 +54,7 @@ struct TestingSetup: public BasicTestingSetup {
|
||||||
fs::path pathTemp;
|
fs::path pathTemp;
|
||||||
boost::thread_group threadGroup;
|
boost::thread_group threadGroup;
|
||||||
CConnman* connman;
|
CConnman* connman;
|
||||||
|
CScheduler scheduler;
|
||||||
|
|
||||||
TestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
|
TestingSetup(const std::string& chainName = CBaseChainParams::MAIN);
|
||||||
~TestingSetup();
|
~TestingSetup();
|
||||||
|
|
|
@ -4,46 +4,123 @@
|
||||||
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
|
||||||
|
|
||||||
#include "validationinterface.h"
|
#include "validationinterface.h"
|
||||||
|
#include "init.h"
|
||||||
|
#include "scheduler.h"
|
||||||
|
#include "sync.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#include <list>
|
||||||
|
#include <atomic>
|
||||||
|
|
||||||
|
#include <boost/signals2/signal.hpp>
|
||||||
|
|
||||||
|
struct MainSignalsInstance {
|
||||||
|
boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
|
||||||
|
boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
|
||||||
|
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef>&)> BlockConnected;
|
||||||
|
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
|
||||||
|
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
|
||||||
|
boost::signals2::signal<void (const uint256 &)> Inventory;
|
||||||
|
boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
|
||||||
|
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
|
||||||
|
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
|
||||||
|
|
||||||
|
// We are not allowed to assume the scheduler only runs in one thread,
|
||||||
|
// but must ensure all callbacks happen in-order, so we end up creating
|
||||||
|
// our own queue here :(
|
||||||
|
SingleThreadedSchedulerClient m_schedulerClient;
|
||||||
|
|
||||||
|
MainSignalsInstance(CScheduler *pscheduler) : m_schedulerClient(pscheduler) {}
|
||||||
|
};
|
||||||
|
|
||||||
static CMainSignals g_signals;
|
static CMainSignals g_signals;
|
||||||
|
|
||||||
|
void CMainSignals::RegisterBackgroundSignalScheduler(CScheduler& scheduler) {
|
||||||
|
assert(!m_internals);
|
||||||
|
m_internals.reset(new MainSignalsInstance(&scheduler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::UnregisterBackgroundSignalScheduler() {
|
||||||
|
m_internals.reset(nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::FlushBackgroundCallbacks() {
|
||||||
|
m_internals->m_schedulerClient.EmptyQueue();
|
||||||
|
}
|
||||||
|
|
||||||
CMainSignals& GetMainSignals()
|
CMainSignals& GetMainSignals()
|
||||||
{
|
{
|
||||||
return g_signals;
|
return g_signals;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
|
void RegisterValidationInterface(CValidationInterface* pwalletIn) {
|
||||||
g_signals.UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
|
g_signals.m_internals->UpdatedBlockTip.connect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
|
||||||
g_signals.TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
|
g_signals.m_internals->TransactionAddedToMempool.connect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
|
||||||
g_signals.BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
|
g_signals.m_internals->BlockConnected.connect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
|
||||||
g_signals.BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
|
g_signals.m_internals->BlockDisconnected.connect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
|
||||||
g_signals.SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
|
g_signals.m_internals->SetBestChain.connect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
|
||||||
g_signals.Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
g_signals.m_internals->Inventory.connect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
||||||
g_signals.Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
|
g_signals.m_internals->Broadcast.connect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
|
||||||
g_signals.BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
g_signals.m_internals->BlockChecked.connect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
||||||
g_signals.NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
|
g_signals.m_internals->NewPoWValidBlock.connect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
|
void UnregisterValidationInterface(CValidationInterface* pwalletIn) {
|
||||||
g_signals.BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
g_signals.m_internals->BlockChecked.disconnect(boost::bind(&CValidationInterface::BlockChecked, pwalletIn, _1, _2));
|
||||||
g_signals.Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
|
g_signals.m_internals->Broadcast.disconnect(boost::bind(&CValidationInterface::ResendWalletTransactions, pwalletIn, _1, _2));
|
||||||
g_signals.Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
g_signals.m_internals->Inventory.disconnect(boost::bind(&CValidationInterface::Inventory, pwalletIn, _1));
|
||||||
g_signals.SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
|
g_signals.m_internals->SetBestChain.disconnect(boost::bind(&CValidationInterface::SetBestChain, pwalletIn, _1));
|
||||||
g_signals.TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
|
g_signals.m_internals->TransactionAddedToMempool.disconnect(boost::bind(&CValidationInterface::TransactionAddedToMempool, pwalletIn, _1));
|
||||||
g_signals.BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
|
g_signals.m_internals->BlockConnected.disconnect(boost::bind(&CValidationInterface::BlockConnected, pwalletIn, _1, _2, _3));
|
||||||
g_signals.BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
|
g_signals.m_internals->BlockDisconnected.disconnect(boost::bind(&CValidationInterface::BlockDisconnected, pwalletIn, _1));
|
||||||
g_signals.UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
|
g_signals.m_internals->UpdatedBlockTip.disconnect(boost::bind(&CValidationInterface::UpdatedBlockTip, pwalletIn, _1, _2, _3));
|
||||||
g_signals.NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
|
g_signals.m_internals->NewPoWValidBlock.disconnect(boost::bind(&CValidationInterface::NewPoWValidBlock, pwalletIn, _1, _2));
|
||||||
}
|
}
|
||||||
|
|
||||||
void UnregisterAllValidationInterfaces() {
|
void UnregisterAllValidationInterfaces() {
|
||||||
g_signals.BlockChecked.disconnect_all_slots();
|
g_signals.m_internals->BlockChecked.disconnect_all_slots();
|
||||||
g_signals.Broadcast.disconnect_all_slots();
|
g_signals.m_internals->Broadcast.disconnect_all_slots();
|
||||||
g_signals.Inventory.disconnect_all_slots();
|
g_signals.m_internals->Inventory.disconnect_all_slots();
|
||||||
g_signals.SetBestChain.disconnect_all_slots();
|
g_signals.m_internals->SetBestChain.disconnect_all_slots();
|
||||||
g_signals.TransactionAddedToMempool.disconnect_all_slots();
|
g_signals.m_internals->TransactionAddedToMempool.disconnect_all_slots();
|
||||||
g_signals.BlockConnected.disconnect_all_slots();
|
g_signals.m_internals->BlockConnected.disconnect_all_slots();
|
||||||
g_signals.BlockDisconnected.disconnect_all_slots();
|
g_signals.m_internals->BlockDisconnected.disconnect_all_slots();
|
||||||
g_signals.UpdatedBlockTip.disconnect_all_slots();
|
g_signals.m_internals->UpdatedBlockTip.disconnect_all_slots();
|
||||||
g_signals.NewPoWValidBlock.disconnect_all_slots();
|
g_signals.m_internals->NewPoWValidBlock.disconnect_all_slots();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {
|
||||||
|
m_internals->UpdatedBlockTip(pindexNew, pindexFork, fInitialDownload);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::TransactionAddedToMempool(const CTransactionRef &ptx) {
|
||||||
|
m_internals->TransactionAddedToMempool(ptx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::BlockConnected(const std::shared_ptr<const CBlock> &pblock, const CBlockIndex *pindex, const std::vector<CTransactionRef>& vtxConflicted) {
|
||||||
|
m_internals->BlockConnected(pblock, pindex, vtxConflicted);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::BlockDisconnected(const std::shared_ptr<const CBlock> &pblock) {
|
||||||
|
m_internals->BlockDisconnected(pblock);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::SetBestChain(const CBlockLocator &locator) {
|
||||||
|
m_internals->SetBestChain(locator);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::Inventory(const uint256 &hash) {
|
||||||
|
m_internals->Inventory(hash);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::Broadcast(int64_t nBestBlockTime, CConnman* connman) {
|
||||||
|
m_internals->Broadcast(nBestBlockTime, connman);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::BlockChecked(const CBlock& block, const CValidationState& state) {
|
||||||
|
m_internals->BlockChecked(block, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CMainSignals::NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock> &block) {
|
||||||
|
m_internals->NewPoWValidBlock(pindex, block);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
#ifndef BITCOIN_VALIDATIONINTERFACE_H
|
#ifndef BITCOIN_VALIDATIONINTERFACE_H
|
||||||
#define BITCOIN_VALIDATIONINTERFACE_H
|
#define BITCOIN_VALIDATIONINTERFACE_H
|
||||||
|
|
||||||
#include <boost/signals2/signal.hpp>
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
|
||||||
#include "primitives/transaction.h" // CTransaction(Ref)
|
#include "primitives/transaction.h" // CTransaction(Ref)
|
||||||
|
@ -20,6 +19,7 @@ class CReserveScript;
|
||||||
class CValidationInterface;
|
class CValidationInterface;
|
||||||
class CValidationState;
|
class CValidationState;
|
||||||
class uint256;
|
class uint256;
|
||||||
|
class CScheduler;
|
||||||
|
|
||||||
// These functions dispatch to one or all registered wallets
|
// These functions dispatch to one or all registered wallets
|
||||||
|
|
||||||
|
@ -32,49 +32,66 @@ void UnregisterAllValidationInterfaces();
|
||||||
|
|
||||||
class CValidationInterface {
|
class CValidationInterface {
|
||||||
protected:
|
protected:
|
||||||
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
|
|
||||||
virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {}
|
|
||||||
virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {}
|
|
||||||
virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {}
|
|
||||||
virtual void SetBestChain(const CBlockLocator &locator) {}
|
|
||||||
virtual void Inventory(const uint256 &hash) {}
|
|
||||||
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
|
|
||||||
virtual void BlockChecked(const CBlock&, const CValidationState&) {}
|
|
||||||
virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {};
|
|
||||||
friend void ::RegisterValidationInterface(CValidationInterface*);
|
|
||||||
friend void ::UnregisterValidationInterface(CValidationInterface*);
|
|
||||||
friend void ::UnregisterAllValidationInterfaces();
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CMainSignals {
|
|
||||||
/** Notifies listeners of updated block chain tip */
|
/** Notifies listeners of updated block chain tip */
|
||||||
boost::signals2::signal<void (const CBlockIndex *, const CBlockIndex *, bool fInitialDownload)> UpdatedBlockTip;
|
virtual void UpdatedBlockTip(const CBlockIndex *pindexNew, const CBlockIndex *pindexFork, bool fInitialDownload) {}
|
||||||
/** Notifies listeners of a transaction having been added to mempool. */
|
/** Notifies listeners of a transaction having been added to mempool. */
|
||||||
boost::signals2::signal<void (const CTransactionRef &)> TransactionAddedToMempool;
|
virtual void TransactionAddedToMempool(const CTransactionRef &ptxn) {}
|
||||||
/**
|
/**
|
||||||
* Notifies listeners of a block being connected.
|
* Notifies listeners of a block being connected.
|
||||||
* Provides a vector of transactions evicted from the mempool as a result.
|
* Provides a vector of transactions evicted from the mempool as a result.
|
||||||
*/
|
*/
|
||||||
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &)> BlockConnected;
|
virtual void BlockConnected(const std::shared_ptr<const CBlock> &block, const CBlockIndex *pindex, const std::vector<CTransactionRef> &txnConflicted) {}
|
||||||
/** Notifies listeners of a block being disconnected */
|
/** Notifies listeners of a block being disconnected */
|
||||||
boost::signals2::signal<void (const std::shared_ptr<const CBlock> &)> BlockDisconnected;
|
virtual void BlockDisconnected(const std::shared_ptr<const CBlock> &block) {}
|
||||||
/** Notifies listeners of a new active block chain. */
|
/** Notifies listeners of the new active block chain on-disk. */
|
||||||
boost::signals2::signal<void (const CBlockLocator &)> SetBestChain;
|
virtual void SetBestChain(const CBlockLocator &locator) {}
|
||||||
/** Notifies listeners about an inventory item being seen on the network. */
|
/** Notifies listeners about an inventory item being seen on the network. */
|
||||||
boost::signals2::signal<void (const uint256 &)> Inventory;
|
virtual void Inventory(const uint256 &hash) {}
|
||||||
/** Tells listeners to broadcast their data. */
|
/** Tells listeners to broadcast their data. */
|
||||||
boost::signals2::signal<void (int64_t nBestBlockTime, CConnman* connman)> Broadcast;
|
virtual void ResendWalletTransactions(int64_t nBestBlockTime, CConnman* connman) {}
|
||||||
/**
|
/**
|
||||||
* Notifies listeners of a block validation result.
|
* Notifies listeners of a block validation result.
|
||||||
* If the provided CValidationState IsValid, the provided block
|
* If the provided CValidationState IsValid, the provided block
|
||||||
* is guaranteed to be the current best block at the time the
|
* is guaranteed to be the current best block at the time the
|
||||||
* callback was generated (not necessarily now)
|
* callback was generated (not necessarily now)
|
||||||
*/
|
*/
|
||||||
boost::signals2::signal<void (const CBlock&, const CValidationState&)> BlockChecked;
|
virtual void BlockChecked(const CBlock&, const CValidationState&) {}
|
||||||
/**
|
/**
|
||||||
* Notifies listeners that a block which builds directly on our current tip
|
* Notifies listeners that a block which builds directly on our current tip
|
||||||
* has been received and connected to the headers tree, though not validated yet */
|
* has been received and connected to the headers tree, though not validated yet */
|
||||||
boost::signals2::signal<void (const CBlockIndex *, const std::shared_ptr<const CBlock>&)> NewPoWValidBlock;
|
virtual void NewPoWValidBlock(const CBlockIndex *pindex, const std::shared_ptr<const CBlock>& block) {};
|
||||||
|
friend void ::RegisterValidationInterface(CValidationInterface*);
|
||||||
|
friend void ::UnregisterValidationInterface(CValidationInterface*);
|
||||||
|
friend void ::UnregisterAllValidationInterfaces();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct MainSignalsInstance;
|
||||||
|
class CMainSignals {
|
||||||
|
private:
|
||||||
|
std::unique_ptr<MainSignalsInstance> m_internals;
|
||||||
|
|
||||||
|
friend void ::RegisterValidationInterface(CValidationInterface*);
|
||||||
|
friend void ::UnregisterValidationInterface(CValidationInterface*);
|
||||||
|
friend void ::UnregisterAllValidationInterfaces();
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Register a CScheduler to give callbacks which should run in the background (may only be called once) */
|
||||||
|
void RegisterBackgroundSignalScheduler(CScheduler& scheduler);
|
||||||
|
/** Unregister a CScheduler to give callbacks which should run in the background - these callbacks will now be dropped! */
|
||||||
|
void UnregisterBackgroundSignalScheduler();
|
||||||
|
/** Call any remaining callbacks on the calling thread */
|
||||||
|
void FlushBackgroundCallbacks();
|
||||||
|
|
||||||
|
void UpdatedBlockTip(const CBlockIndex *, const CBlockIndex *, bool fInitialDownload);
|
||||||
|
void TransactionAddedToMempool(const CTransactionRef &);
|
||||||
|
void BlockConnected(const std::shared_ptr<const CBlock> &, const CBlockIndex *pindex, const std::vector<CTransactionRef> &);
|
||||||
|
void BlockDisconnected(const std::shared_ptr<const CBlock> &);
|
||||||
|
void UpdatedTransaction(const uint256 &);
|
||||||
|
void SetBestChain(const CBlockLocator &);
|
||||||
|
void Inventory(const uint256 &);
|
||||||
|
void Broadcast(int64_t nBestBlockTime, CConnman* connman);
|
||||||
|
void BlockChecked(const CBlock&, const CValidationState&);
|
||||||
|
void NewPoWValidBlock(const CBlockIndex *, const std::shared_ptr<const CBlock>&);
|
||||||
};
|
};
|
||||||
|
|
||||||
CMainSignals& GetMainSignals();
|
CMainSignals& GetMainSignals();
|
||||||
|
|
Loading…
Reference in a new issue