Fix race in FetchTransactionStore

Because FetchTransactionStore in GetBlockTemplate occasionally accesses
the internal blockchain memory structure while it is being read or modified,
a race can occur. To prevent this, FetchTransactionStore is instead
routed through the internal channel for blockchain requests.
This commit is contained in:
cjepson 2015-03-30 13:16:23 -04:00
parent 4a1445a032
commit 4696d16ed4
2 changed files with 31 additions and 2 deletions

View file

@ -107,6 +107,20 @@ type processBlockResponse struct {
err error
}
// fetchTransactionStoreResponse is a response sent to the reply channel of a
// fetchTransactionStoreMsg.
type fetchTransactionStoreResponse struct {
TxStore blockchain.TxStore
err error
}
// fetchTransactionStoreMsg is a message type to be sent across the message
// channel fetching the tx input store for some Tx.
type fetchTransactionStoreMsg struct {
tx *btcutil.Tx
reply chan fetchTransactionStoreResponse
}
// processBlockMsg is a message type to be sent across the message channel
// for requested a block is processed. Note this call differs from blockMsg
// above in that blockMsg is intended for blocks that came from peers and have
@ -1109,6 +1123,13 @@ out:
err: err,
}
case fetchTransactionStoreMsg:
txStore, err := b.blockChain.FetchTransactionStore(msg.tx)
msg.reply <- fetchTransactionStoreResponse{
TxStore: txStore,
err: err,
}
case processBlockMsg:
isOrphan, err := b.blockChain.ProcessBlock(
msg.block, b.server.timeSource,
@ -1371,6 +1392,15 @@ func (b *blockManager) CalcNextRequiredDifficulty(timestamp time.Time) (uint32,
return response.difficulty, response.err
}
// FetchTransactionStore makes use of FetchTransactionStore on an internal
// instance of a block chain. It is safe for concurrent access.
func (b *blockManager) FetchTransactionStore(tx *btcutil.Tx) (blockchain.TxStore, error) {
reply := make(chan fetchTransactionStoreResponse, 1)
b.msgChan <- fetchTransactionStoreMsg{tx: tx, reply: reply}
response := <-reply
return response.TxStore, response.err
}
// ProcessBlock makes use of ProcessBlock on an internal instance of a block
// chain. It is funneled through the block manager since btcchain is not safe
// for concurrent access.

View file

@ -369,7 +369,6 @@ func NewBlockTemplate(mempool *txMemPool, payToAddress btcutil.Address) (*BlockT
blockManager := mempool.server.blockManager
timeSource := mempool.server.timeSource
chainState := &blockManager.chainState
chain := blockManager.blockChain
// Extend the most recently known best block.
chainState.Lock()
@ -458,7 +457,7 @@ mempoolLoop:
// inputs from the mempool since a transaction which depends on
// other transactions in the mempool must come after those
// dependencies in the final generated block.
txStore, err := chain.FetchTransactionStore(tx)
txStore, err := blockManager.FetchTransactionStore(tx)
if err != nil {
minrLog.Warnf("Unable to fetch transaction store for "+
"tx %s: %v", tx.Sha(), err)