diff --git a/core/state/statedb.go b/core/state/statedb.go index a829d03027..3fba7a24c3 100644 --- a/core/state/statedb.go +++ b/core/state/statedb.go @@ -67,6 +67,7 @@ type StateDB struct { hasher crypto.KeccakState snaps *snapshot.Tree // Nil if snapshot is not available snap snapshot.Snapshot // Nil if snapshot is not available + snapLock sync.Mutex // make the snap account multi-thread safe // originalRoot is the pre-state root, before any changes were made. // It will be updated when the Commit is called. @@ -572,7 +573,11 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject { var data *types.StateAccount if s.snap != nil { start := time.Now() - acc, err := s.snap.Account(crypto.HashData(s.hasher, addr.Bytes())) + // hasher need to be locked for multi-thread safe. + s.snapLock.Lock() + addrHash := crypto.HashData(s.hasher, addr.Bytes()) + s.snapLock.Unlock() + acc, err := s.snap.Account(addrHash) if metrics.EnabledExpensive { s.SnapshotAccountReads += time.Since(start) } diff --git a/core/txpool/legacypool/legacypool.go b/core/txpool/legacypool/legacypool.go index d2e64614d9..c9941ba284 100644 --- a/core/txpool/legacypool/legacypool.go +++ b/core/txpool/legacypool/legacypool.go @@ -563,8 +563,6 @@ func (pool *LegacyPool) SetGasTip(tip *big.Int) { // Nonce returns the next nonce of an account, with all transactions executable // by the pool already applied on top. func (pool *LegacyPool) Nonce(addr common.Address) uint64 { - pool.mu.RLock() - defer pool.mu.RUnlock() defer func(t0 time.Time) { nonceDurationTimer.Update(time.Since(t0)) }(time.Now()) diff --git a/core/txpool/legacypool/noncer.go b/core/txpool/legacypool/noncer.go index 2c65dd2cae..fc08ee86c7 100644 --- a/core/txpool/legacypool/noncer.go +++ b/core/txpool/legacypool/noncer.go @@ -29,7 +29,7 @@ import ( type noncer struct { fallback *state.StateDB nonces map[common.Address]uint64 - lock sync.Mutex + lock sync.RWMutex } // newNoncer creates a new virtual state database to track the pool nonces. @@ -45,15 +45,19 @@ func newNoncer(statedb *state.StateDB) *noncer { func (txn *noncer) get(addr common.Address) uint64 { // We use mutex for get operation is the underlying // state will mutate db even for read access. - txn.lock.Lock() - defer txn.lock.Unlock() + txn.lock.RLock() + nonce, ok := txn.nonces[addr] + txn.lock.RUnlock() - if _, ok := txn.nonces[addr]; !ok { + if !ok { + // GetNonce is so heavy that we don't want to hold the lock while calling it. if nonce := txn.fallback.GetNonce(addr); nonce != 0 { + txn.lock.Lock() txn.nonces[addr] = nonce + txn.lock.Unlock() } } - return txn.nonces[addr] + return nonce } // set inserts a new virtual nonce into the virtual state database to be returned @@ -68,18 +72,13 @@ func (txn *noncer) set(addr common.Address, nonce uint64) { // setIfLower updates a new virtual nonce into the virtual state database if the // new one is lower. func (txn *noncer) setIfLower(addr common.Address, nonce uint64) { - txn.lock.Lock() - defer txn.lock.Unlock() - - if _, ok := txn.nonces[addr]; !ok { - if nonce := txn.fallback.GetNonce(addr); nonce != 0 { - txn.nonces[addr] = nonce - } - } - if txn.nonces[addr] <= nonce { + currNonce := txn.get(addr) + if currNonce <= nonce { return } + txn.lock.Lock() txn.nonces[addr] = nonce + txn.lock.Unlock() } // setAll sets the nonces for all accounts to the given map.