Merge pull request #5594 from LarryRuane/2022-02-getblocktemplate-locking

Fix csBestBlock/cvBlockChange waiting in rpc/mining
This commit is contained in:
Kris Nuttycombe 2022-03-01 20:08:29 -07:00 committed by GitHub
commit 5141c2971c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 33 additions and 15 deletions

View File

@ -305,7 +305,7 @@ bool static Bind(const CService &addr, unsigned int flags) {
void OnRPCStopped() void OnRPCStopped()
{ {
cvBlockChange.notify_all(); g_best_block_cv.notify_all();
LogPrint("rpc", "RPC stopped.\n"); LogPrint("rpc", "RPC stopped.\n");
} }

View File

@ -66,8 +66,10 @@ BlockMap mapBlockIndex;
CChain chainActive; CChain chainActive;
CBlockIndex *pindexBestHeader = NULL; CBlockIndex *pindexBestHeader = NULL;
static std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block static std::atomic<int64_t> nTimeBestReceived(0); // Used only to inform the wallet of when we last received a block
CWaitableCriticalSection csBestBlock; CWaitableCriticalSection g_best_block_mutex;
CConditionVariable cvBlockChange; CConditionVariable g_best_block_cv;
uint256 g_best_block;
int g_best_block_height;
int nScriptCheckThreads = 0; int nScriptCheckThreads = 0;
std::atomic_bool fImporting(false); std::atomic_bool fImporting(false);
std::atomic_bool fReindex(false); std::atomic_bool fReindex(false);
@ -3757,7 +3759,12 @@ void static UpdateTip(CBlockIndex *pindexNew, const CChainParams& chainParams) {
RenderPoolMetrics("sapling", saplingPool); RenderPoolMetrics("sapling", saplingPool);
RenderPoolMetrics("transparent", transparentPool); RenderPoolMetrics("transparent", transparentPool);
cvBlockChange.notify_all(); {
boost::unique_lock<boost::mutex> lock(g_best_block_mutex);
g_best_block = pindexNew->GetBlockHash();
g_best_block_height = pindexNew->nHeight;
g_best_block_cv.notify_all();
}
} }
/** /**

View File

@ -160,8 +160,19 @@ extern BlockMap mapBlockIndex;
extern std::optional<uint64_t> last_block_num_txs; extern std::optional<uint64_t> last_block_num_txs;
extern std::optional<uint64_t> last_block_size; extern std::optional<uint64_t> last_block_size;
extern const std::string strMessageMagic; extern const std::string strMessageMagic;
extern CWaitableCriticalSection csBestBlock;
extern CConditionVariable cvBlockChange; //! These four variables are used to notify getblocktemplate RPC of new tips.
//! When UpdateTip() establishes a new tip (best block), it must awaken a
//! waiting getblocktemplate RPC (if there is one) immediately. But upon waking
//! up, getblocktemplate cannot call chainActive->Tip() because it does not
//! (and cannot) hold cs_main. So the g_best_block_height and g_best_block variables
//! (protected by g_best_block_mutex) provide the needed height and block
//! hash respectively to getblocktemplate without it requiring cs_main.
extern CWaitableCriticalSection g_best_block_mutex;
extern CConditionVariable g_best_block_cv;
extern int g_best_block_height;
extern uint256 g_best_block;
extern std::atomic_bool fImporting; extern std::atomic_bool fImporting;
extern std::atomic_bool fReindex; extern std::atomic_bool fReindex;
extern int nScriptCheckThreads; extern int nScriptCheckThreads;

View File

@ -607,15 +607,15 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
nTransactionsUpdatedLastLP = nTransactionsUpdatedLast; nTransactionsUpdatedLastLP = nTransactionsUpdatedLast;
} }
// Release the main lock while waiting
// Don't call chainActive->Tip() without holding cs_main
LEAVE_CRITICAL_SECTION(cs_main);
{ {
checktxtime = boost::get_system_time() + boost::posix_time::seconds(10); checktxtime = boost::get_system_time() + boost::posix_time::seconds(10);
boost::unique_lock<boost::mutex> lock(csBestBlock); boost::unique_lock<boost::mutex> lock(g_best_block_mutex);
while (chainActive.Tip()->GetBlockHash() == hashWatchedChain && IsRPCRunning()) while (g_best_block == hashWatchedChain && IsRPCRunning())
{ {
// Release the main lock while waiting
LEAVE_CRITICAL_SECTION(cs_main);
// Before waiting, generate the coinbase for the block following the next // Before waiting, generate the coinbase for the block following the next
// block (since this is cpu-intensive), so that when next block arrives, // block (since this is cpu-intensive), so that when next block arrives,
// we can quickly respond with a template for following block. // we can quickly respond with a template for following block.
@ -628,12 +628,11 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
Params(), CAmount{0}, minerAddress, cached_next_cb_height); Params(), CAmount{0}, minerAddress, cached_next_cb_height);
next_cb_mtx = cached_next_cb_mtx; next_cb_mtx = cached_next_cb_mtx;
} }
bool timedout = !cvBlockChange.timed_wait(lock, checktxtime); bool timedout = !g_best_block_cv.timed_wait(lock, checktxtime);
ENTER_CRITICAL_SECTION(cs_main);
// Optimization: even if timed out, a new block may have arrived // Optimization: even if timed out, a new block may have arrived
// while waiting for cs_main; if so, don't discard next_cb_mtx. // while waiting for cs_main; if so, don't discard next_cb_mtx.
if (chainActive.Tip()->GetBlockHash() != hashWatchedChain) break; if (g_best_block != hashWatchedChain) break;
// Timeout: Check transactions for update // Timeout: Check transactions for update
if (timedout && mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) { if (timedout && mempool.GetTransactionsUpdated() != nTransactionsUpdatedLastLP) {
@ -643,11 +642,12 @@ UniValue getblocktemplate(const UniValue& params, bool fHelp)
} }
checktxtime += boost::posix_time::seconds(10); checktxtime += boost::posix_time::seconds(10);
} }
if (chainActive.Tip()->nHeight != nHeight + 1) { if (g_best_block_height != nHeight + 1) {
// Unexpected height (reorg or >1 blocks arrived while waiting) invalidates coinbase tx. // Unexpected height (reorg or >1 blocks arrived while waiting) invalidates coinbase tx.
next_cb_mtx = nullopt; next_cb_mtx = nullopt;
} }
} }
ENTER_CRITICAL_SECTION(cs_main);
if (!IsRPCRunning()) if (!IsRPCRunning())
throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down"); throw JSONRPCError(RPC_CLIENT_NOT_CONNECTED, "Shutting down");