Solve chainActive-related locking issues

- In wallet and GUI code LOCK cs_main as well as cs_wallet when
  necessary
- In main.cpp SendMessages move the TRY_LOCK(cs_main) up, to encompass the call
  to IsInitialBlockDownload.
- Make ActivateBestChain, AddToBlockIndex, IsInitialBlockDownload,
  InitBlockIndex acquire the cs_main lock

Fixes #3997
This commit is contained in:
Wladimir J. van der Laan 2014-04-15 17:38:25 +02:00
parent e07c943ce8
commit 55a1db4fa2
7 changed files with 269 additions and 272 deletions

View file

@ -1310,7 +1310,7 @@ int GetNumBlocksOfPeers()
bool IsInitialBlockDownload()
{
AssertLockHeld(cs_main);
LOCK(cs_main);
if (fImporting || fReindex || chainActive.Height() < Checkpoints::GetTotalBlocksEstimate())
return true;
static int64_t nLastUpdate;
@ -2087,7 +2087,7 @@ void static FindMostWorkChain() {
// Try to activate to the most-work chain (thereby connecting it).
bool ActivateBestChain(CValidationState &state) {
AssertLockHeld(cs_main);
LOCK(cs_main);
CBlockIndex *pindexOldTip = chainActive.Tip();
bool fComplete = false;
while (!fComplete) {
@ -2136,7 +2136,6 @@ bool ActivateBestChain(CValidationState &state) {
bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos& pos)
{
AssertLockHeld(cs_main);
// Check for duplicate
uint256 hash = block.GetHash();
if (mapBlockIndex.count(hash))
@ -2173,6 +2172,7 @@ bool AddToBlockIndex(CBlock& block, CValidationState& state, const CDiskBlockPos
if (!ActivateBestChain(state))
return false;
LOCK(cs_main);
if (pindexNew == chainActive.Tip())
{
// Clear fork warning if its no longer applicable
@ -2962,6 +2962,7 @@ bool LoadBlockIndex()
bool InitBlockIndex() {
LOCK(cs_main);
// Check whether we're already initialized
if (chainActive.Genesis() != NULL)
return true;
@ -4201,6 +4202,10 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
}
}
TRY_LOCK(cs_main, lockMain); // Acquire cs_main for IsInitialBlockDownload() and CNodeState()
if (!lockMain)
return true;
// Address refresh broadcast
static int64_t nLastRebroadcast;
if (!IsInitialBlockDownload() && (GetTime() - nLastRebroadcast > 24 * 60 * 60))
@ -4251,10 +4256,6 @@ bool SendMessages(CNode* pto, bool fSendTrickle)
pto->PushMessage("addr", vAddr);
}
TRY_LOCK(cs_main, lockMain);
if (!lockMain)
return true;
CNodeState &state = *State(pto->GetId());
if (state.fShouldBan) {
if (pto->addr.IsLocal())

View file

@ -55,6 +55,7 @@ int ClientModel::getNumConnections(unsigned int flags) const
int ClientModel::getNumBlocks() const
{
LOCK(cs_main);
return chainActive.Height();
}
@ -76,6 +77,7 @@ quint64 ClientModel::getTotalBytesSent() const
QDateTime ClientModel::getLastBlockDate() const
{
LOCK(cs_main);
if (chainActive.Tip())
return QDateTime::fromTime_t(chainActive.Tip()->GetBlockTime());
else
@ -84,6 +86,7 @@ QDateTime ClientModel::getLastBlockDate() const
double ClientModel::getVerificationProgress() const
{
LOCK(cs_main);
return Checkpoints::GuessVerificationProgress(chainActive.Tip());
}

View file

@ -46,8 +46,7 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u
{
QString strHTML;
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
strHTML.reserve(4000);
strHTML += "<html><font face='verdana, arial, helvetica, sans-serif'>";
@ -272,8 +271,6 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u
strHTML += "<br><b>" + tr("Inputs") + ":</b>";
strHTML += "<ul>";
{
LOCK(wallet->cs_wallet);
BOOST_FOREACH(const CTxIn& txin, wtx.vin)
{
COutPoint prevout = txin.prevout;
@ -297,12 +294,10 @@ QString TransactionDesc::toHTML(CWallet *wallet, CWalletTx &wtx, int vout, int u
}
}
}
}
strHTML += "</ul>";
}
strHTML += "</font></html>";
}
return strHTML;
}

View file

@ -78,7 +78,7 @@ public:
qDebug() << "TransactionTablePriv::refreshWallet";
cachedWallet.clear();
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
for(std::map<uint256, CWalletTx>::iterator it = wallet->mapWallet.begin(); it != wallet->mapWallet.end(); ++it)
{
if(TransactionRecord::showTransaction(it->second))
@ -96,7 +96,7 @@ public:
{
qDebug() << "TransactionTablePriv::updateWallet : " + QString::fromStdString(hash.ToString()) + " " + QString::number(status);
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
// Find transaction in wallet
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(hash);
@ -190,10 +190,9 @@ public:
// If a status update is needed (blocks came in since last check),
// update the status of this transaction from the wallet. Otherwise,
// simply re-use the cached status.
LOCK2(cs_main, wallet->cs_wallet);
if(rec->statusUpdateNeeded())
{
{
LOCK(wallet->cs_wallet);
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
if(mi != wallet->mapWallet.end())
@ -201,7 +200,6 @@ public:
rec->updateStatus(mi->second);
}
}
}
return rec;
}
else
@ -213,7 +211,7 @@ public:
QString describe(TransactionRecord *rec, int unit)
{
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
std::map<uint256, CWalletTx>::iterator mi = wallet->mapWallet.find(rec->hash);
if(mi != wallet->mapWallet.end())
{
@ -228,17 +226,12 @@ TransactionTableModel::TransactionTableModel(CWallet* wallet, WalletModel *paren
QAbstractTableModel(parent),
wallet(wallet),
walletModel(parent),
priv(new TransactionTablePriv(wallet, this)),
cachedNumBlocks(0)
priv(new TransactionTablePriv(wallet, this))
{
columns << QString() << tr("Date") << tr("Type") << tr("Address") << tr("Amount");
priv->refreshWallet();
QTimer *timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(updateConfirmations()));
timer->start(MODEL_UPDATE_DELAY);
connect(walletModel->getOptionsModel(), SIGNAL(displayUnitChanged(int)), this, SLOT(updateDisplayUnit()));
}
@ -257,16 +250,12 @@ void TransactionTableModel::updateTransaction(const QString &hash, int status)
void TransactionTableModel::updateConfirmations()
{
if(chainActive.Height() != cachedNumBlocks)
{
cachedNumBlocks = chainActive.Height();
// Blocks came in since last poll.
// Invalidate status (number of confirmations) and (possibly) description
// for all rows. Qt is smart enough to only actually request the data for the
// visible rows.
emit dataChanged(index(0, Status), index(priv->size()-1, Status));
emit dataChanged(index(0, ToAddress), index(priv->size()-1, ToAddress));
}
}
int TransactionTableModel::rowCount(const QModelIndex &parent) const

View file

@ -69,7 +69,6 @@ private:
WalletModel *walletModel;
QStringList columns;
TransactionTablePriv *priv;
int cachedNumBlocks;
QString lookupAddress(const std::string &address, bool tooltip) const;
QVariant addressColor(const TransactionRecord *wtx) const;

View file

@ -98,11 +98,21 @@ void WalletModel::updateStatus()
void WalletModel::pollBalanceChanged()
{
bool heightChanged = false;
{
LOCK(cs_main);
if(chainActive.Height() != cachedNumBlocks)
{
// Balance and number of transactions might have changed
cachedNumBlocks = chainActive.Height();
heightChanged = true;
}
}
if(heightChanged)
{
checkBalanceChanged();
if(transactionTableModel)
transactionTableModel->updateConfirmations();
}
}
@ -520,7 +530,7 @@ bool WalletModel::getPubKey(const CKeyID &address, CPubKey& vchPubKeyOut) const
// returns a list of COutputs from COutPoints
void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vector<COutput>& vOutputs)
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
BOOST_FOREACH(const COutPoint& outpoint, vOutpoints)
{
if (!wallet->mapWallet.count(outpoint.hash)) continue;
@ -533,7 +543,7 @@ void WalletModel::getOutputs(const std::vector<COutPoint>& vOutpoints, std::vect
bool WalletModel::isSpent(const COutPoint& outpoint) const
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
return wallet->IsSpent(outpoint.hash, outpoint.n);
}
@ -543,7 +553,7 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
std::vector<COutput> vCoins;
wallet->AvailableCoins(vCoins);
LOCK(wallet->cs_wallet); // ListLockedCoins, mapWallet
LOCK2(cs_main, wallet->cs_wallet); // ListLockedCoins, mapWallet
std::vector<COutPoint> vLockedCoins;
wallet->ListLockedCoins(vLockedCoins);
@ -575,25 +585,25 @@ void WalletModel::listCoins(std::map<QString, std::vector<COutput> >& mapCoins)
bool WalletModel::isLockedCoin(uint256 hash, unsigned int n) const
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
return wallet->IsLockedCoin(hash, n);
}
void WalletModel::lockCoin(COutPoint& output)
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
wallet->LockCoin(output);
}
void WalletModel::unlockCoin(COutPoint& output)
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
wallet->UnlockCoin(output);
}
void WalletModel::listLockedCoins(std::vector<COutPoint>& vOutpts)
{
LOCK(wallet->cs_wallet);
LOCK2(cs_main, wallet->cs_wallet);
wallet->ListLockedCoins(vOutpts);
}

View file

@ -606,7 +606,7 @@ bool CWallet::AddToWalletIfInvolvingMe(const uint256 &hash, const CTransaction&
void CWallet::SyncTransaction(const uint256 &hash, const CTransaction& tx, const CBlock* pblock)
{
LOCK(cs_wallet);
LOCK2(cs_main, cs_wallet);
if (!AddToWalletIfInvolvingMe(hash, tx, pblock, true))
return; // Not one of ours
@ -834,7 +834,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
CBlockIndex* pindex = pindexStart;
{
LOCK(cs_wallet);
LOCK2(cs_main, cs_wallet);
// no need to read and scan block, if block was created before
// our wallet birthday (as adjusted for block time variability)
@ -869,7 +869,7 @@ int CWallet::ScanForWalletTransactions(CBlockIndex* pindexStart, bool fUpdate)
void CWallet::ReacceptWalletTransactions()
{
LOCK(cs_wallet);
LOCK2(cs_main, cs_wallet);
BOOST_FOREACH(PAIRTYPE(const uint256, CWalletTx)& item, mapWallet)
{
const uint256& wtxid = item.first;
@ -964,7 +964,7 @@ int64_t CWallet::GetBalance() const
{
int64_t nTotal = 0;
{
LOCK(cs_wallet);
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
@ -980,7 +980,7 @@ int64_t CWallet::GetUnconfirmedBalance() const
{
int64_t nTotal = 0;
{
LOCK(cs_wallet);
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;
@ -995,7 +995,7 @@ int64_t CWallet::GetImmatureBalance() const
{
int64_t nTotal = 0;
{
LOCK(cs_wallet);
LOCK2(cs_main, cs_wallet);
for (map<uint256, CWalletTx>::const_iterator it = mapWallet.begin(); it != mapWallet.end(); ++it)
{
const CWalletTx* pcoin = &(*it).second;