mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-18 18:02:24 +00:00
miner: Add Work.stateMu to protect Work.state.
Previously, the `Work.state` was protected by the `Worker.currentMu`. However, when the worker sends work to the `Agent` and the `Agent` sends the work back to the worker via the `recv` channel, there is no mutex available to protect access/mutation. The worker calls `WriteBlockWithState()` which in turn calls `Commit()` which then clears `StateDB.stateObjectsDirty` without a mutex. That causes the panic when iterating over the map in `Copy()`. This commit adds a `stateMu` mutex to the `Work` type so the mutex can follow the work between the agent & worker.
This commit is contained in:
parent
f9c456e02d
commit
0c60ba5f6c
1 changed files with 23 additions and 4 deletions
|
|
@ -66,6 +66,7 @@ type Work struct {
|
||||||
config *params.ChainConfig
|
config *params.ChainConfig
|
||||||
signer types.Signer
|
signer types.Signer
|
||||||
|
|
||||||
|
stateMu sync.RWMutex // protects state
|
||||||
state *state.StateDB // apply state changes here
|
state *state.StateDB // apply state changes here
|
||||||
ancestors *set.Set // ancestor set (used for checking uncle parent validity)
|
ancestors *set.Set // ancestor set (used for checking uncle parent validity)
|
||||||
family *set.Set // family set (used for checking uncle invalidity)
|
family *set.Set // family set (used for checking uncle invalidity)
|
||||||
|
|
@ -184,6 +185,8 @@ func (self *worker) pending() (*types.Block, *state.StateDB) {
|
||||||
|
|
||||||
self.currentMu.Lock()
|
self.currentMu.Lock()
|
||||||
defer self.currentMu.Unlock()
|
defer self.currentMu.Unlock()
|
||||||
|
self.current.stateMu.RLock()
|
||||||
|
defer self.current.stateMu.RUnlock()
|
||||||
return self.current.Block, self.current.state.Copy()
|
return self.current.Block, self.current.state.Copy()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -263,12 +266,16 @@ func (self *worker) update() {
|
||||||
// Apply transaction to the pending state if we're not mining
|
// Apply transaction to the pending state if we're not mining
|
||||||
if atomic.LoadInt32(&self.mining) == 0 {
|
if atomic.LoadInt32(&self.mining) == 0 {
|
||||||
self.currentMu.Lock()
|
self.currentMu.Lock()
|
||||||
|
self.current.stateMu.Lock()
|
||||||
|
|
||||||
acc, _ := types.Sender(self.current.signer, ev.Tx)
|
acc, _ := types.Sender(self.current.signer, ev.Tx)
|
||||||
txs := map[common.Address]types.Transactions{acc: {ev.Tx}}
|
txs := map[common.Address]types.Transactions{acc: {ev.Tx}}
|
||||||
txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)
|
txset := types.NewTransactionsByPriceAndNonce(self.current.signer, txs)
|
||||||
|
|
||||||
self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
|
self.current.commitTransactions(self.mux, txset, self.chain, self.coinbase)
|
||||||
self.updateSnapshot()
|
self.updateSnapshot()
|
||||||
|
|
||||||
|
self.current.stateMu.Unlock()
|
||||||
self.currentMu.Unlock()
|
self.currentMu.Unlock()
|
||||||
} else {
|
} else {
|
||||||
// If we're mining, but nothing is being processed, wake on new transactions
|
// If we're mining, but nothing is being processed, wake on new transactions
|
||||||
|
|
@ -307,6 +314,8 @@ func (self *worker) wait() {
|
||||||
l.BlockHash = block.Hash()
|
l.BlockHash = block.Hash()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
work.stateMu.Lock()
|
||||||
for _, log := range work.state.Logs() {
|
for _, log := range work.state.Logs() {
|
||||||
log.BlockHash = block.Hash()
|
log.BlockHash = block.Hash()
|
||||||
}
|
}
|
||||||
|
|
@ -315,6 +324,8 @@ func (self *worker) wait() {
|
||||||
log.Error("Failed writing block to chain", "err", err)
|
log.Error("Failed writing block to chain", "err", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
work.stateMu.Unlock()
|
||||||
|
|
||||||
// check if canon block and write transactions
|
// check if canon block and write transactions
|
||||||
if stat == core.CanonStatTy {
|
if stat == core.CanonStatTy {
|
||||||
// implicit by posting ChainHeadEvent
|
// implicit by posting ChainHeadEvent
|
||||||
|
|
@ -322,10 +333,13 @@ func (self *worker) wait() {
|
||||||
}
|
}
|
||||||
// Broadcast the block and announce chain insertion event
|
// Broadcast the block and announce chain insertion event
|
||||||
self.mux.Post(core.NewMinedBlockEvent{Block: block})
|
self.mux.Post(core.NewMinedBlockEvent{Block: block})
|
||||||
var (
|
|
||||||
events []interface{}
|
var events []interface{}
|
||||||
logs = work.state.Logs()
|
|
||||||
)
|
work.stateMu.RLock()
|
||||||
|
logs := work.state.Logs()
|
||||||
|
work.stateMu.RUnlock()
|
||||||
|
|
||||||
events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
|
events = append(events, core.ChainEvent{Block: block, Hash: block.Hash(), Logs: logs})
|
||||||
if stat == core.CanonStatTy {
|
if stat == core.CanonStatTy {
|
||||||
events = append(events, core.ChainHeadEvent{Block: block})
|
events = append(events, core.ChainHeadEvent{Block: block})
|
||||||
|
|
@ -444,6 +458,11 @@ func (self *worker) commitNewWork() {
|
||||||
log.Error("Failed to create mining context", "err", err)
|
log.Error("Failed to create mining context", "err", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Obtain current work's state lock after we receive new work assignment.
|
||||||
|
self.current.stateMu.Lock()
|
||||||
|
defer self.current.stateMu.Unlock()
|
||||||
|
|
||||||
// Create the current work task and check any fork transitions needed
|
// Create the current work task and check any fork transitions needed
|
||||||
work := self.current
|
work := self.current
|
||||||
if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
|
if self.config.DAOForkSupport && self.config.DAOForkBlock != nil && self.config.DAOForkBlock.Cmp(header.Number) == 0 {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue