Skip to content

Commit f832375

Browse files
holimankaralabe
authored andcommitted
core: make current*Block atomic, and accessor functions mutex-free (#16171)
* core: make current*Block atomic, and accessor functions mutex-free * core: fix review concerns * core: fix error in atomic assignment * core/light: implement atomic getter/setter for headerchain
1 parent d398d04 commit f832375

File tree

3 files changed

+76
-81
lines changed

3 files changed

+76
-81
lines changed

core/blockchain.go

Lines changed: 55 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ type BlockChain struct {
107107
procmu sync.RWMutex // block processor lock
108108

109109
checkpoint int // checkpoint counts towards the new checkpoint
110-
currentBlock *types.Block // Current head of the block chain
111-
currentFastBlock *types.Block // Current head of the fast-sync chain (may be above the block chain!)
110+
currentBlock atomic.Value // Current head of the block chain
111+
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
112112

113113
stateCache state.Database // State database to reuse between imports (contains state cache)
114114
bodyCache *lru.Cache // Cache for the most recent block bodies
@@ -224,10 +224,10 @@ func (bc *BlockChain) loadLastState() error {
224224
}
225225
}
226226
// Everything seems to be fine, set as the head block
227-
bc.currentBlock = currentBlock
227+
bc.currentBlock.Store(currentBlock)
228228

229229
// Restore the last known head header
230-
currentHeader := bc.currentBlock.Header()
230+
currentHeader := currentBlock.Header()
231231
if head := GetHeadHeaderHash(bc.db); head != (common.Hash{}) {
232232
if header := bc.GetHeaderByHash(head); header != nil {
233233
currentHeader = header
@@ -236,21 +236,23 @@ func (bc *BlockChain) loadLastState() error {
236236
bc.hc.SetCurrentHeader(currentHeader)
237237

238238
// Restore the last known head fast block
239-
bc.currentFastBlock = bc.currentBlock
239+
bc.currentFastBlock.Store(currentBlock)
240240
if head := GetHeadFastBlockHash(bc.db); head != (common.Hash{}) {
241241
if block := bc.GetBlockByHash(head); block != nil {
242-
bc.currentFastBlock = block
242+
bc.currentFastBlock.Store(block)
243243
}
244244
}
245245

246246
// Issue a status log for the user
247+
currentFastBlock := bc.CurrentFastBlock()
248+
247249
headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
248-
blockTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
249-
fastTd := bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64())
250+
blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
251+
fastTd := bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64())
250252

251253
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd)
252-
log.Info("Loaded most recent local full block", "number", bc.currentBlock.Number(), "hash", bc.currentBlock.Hash(), "td", blockTd)
253-
log.Info("Loaded most recent local fast block", "number", bc.currentFastBlock.Number(), "hash", bc.currentFastBlock.Hash(), "td", fastTd)
254+
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd)
255+
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd)
254256

255257
return nil
256258
}
@@ -279,30 +281,32 @@ func (bc *BlockChain) SetHead(head uint64) error {
279281
bc.futureBlocks.Purge()
280282

281283
// Rewind the block chain, ensuring we don't end up with a stateless head block
282-
if bc.currentBlock != nil && currentHeader.Number.Uint64() < bc.currentBlock.NumberU64() {
283-
bc.currentBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
284+
if currentBlock := bc.CurrentBlock(); currentBlock != nil && currentHeader.Number.Uint64() < currentBlock.NumberU64() {
285+
bc.currentBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
284286
}
285-
if bc.currentBlock != nil {
286-
if _, err := state.New(bc.currentBlock.Root(), bc.stateCache); err != nil {
287+
if currentBlock := bc.CurrentBlock(); currentBlock != nil {
288+
if _, err := state.New(currentBlock.Root(), bc.stateCache); err != nil {
287289
// Rewound state missing, rolled back to before pivot, reset to genesis
288-
bc.currentBlock = nil
290+
bc.currentBlock.Store(bc.genesisBlock)
289291
}
290292
}
291293
// Rewind the fast block in a simpleton way to the target head
292-
if bc.currentFastBlock != nil && currentHeader.Number.Uint64() < bc.currentFastBlock.NumberU64() {
293-
bc.currentFastBlock = bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64())
294+
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock != nil && currentHeader.Number.Uint64() < currentFastBlock.NumberU64() {
295+
bc.currentFastBlock.Store(bc.GetBlock(currentHeader.Hash(), currentHeader.Number.Uint64()))
294296
}
295297
// If either blocks reached nil, reset to the genesis state
296-
if bc.currentBlock == nil {
297-
bc.currentBlock = bc.genesisBlock
298+
if currentBlock := bc.CurrentBlock(); currentBlock == nil {
299+
bc.currentBlock.Store(bc.genesisBlock)
298300
}
299-
if bc.currentFastBlock == nil {
300-
bc.currentFastBlock = bc.genesisBlock
301+
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock == nil {
302+
bc.currentFastBlock.Store(bc.genesisBlock)
301303
}
302-
if err := WriteHeadBlockHash(bc.db, bc.currentBlock.Hash()); err != nil {
304+
currentBlock := bc.CurrentBlock()
305+
currentFastBlock := bc.CurrentFastBlock()
306+
if err := WriteHeadBlockHash(bc.db, currentBlock.Hash()); err != nil {
303307
log.Crit("Failed to reset head full block", "err", err)
304308
}
305-
if err := WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash()); err != nil {
309+
if err := WriteHeadFastBlockHash(bc.db, currentFastBlock.Hash()); err != nil {
306310
log.Crit("Failed to reset head fast block", "err", err)
307311
}
308312
return bc.loadLastState()
@@ -321,7 +325,7 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
321325
}
322326
// If all checks out, manually set the head block
323327
bc.mu.Lock()
324-
bc.currentBlock = block
328+
bc.currentBlock.Store(block)
325329
bc.mu.Unlock()
326330

327331
log.Info("Committed new head block", "number", block.Number(), "hash", hash)
@@ -330,28 +334,19 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
330334

331335
// GasLimit returns the gas limit of the current HEAD block.
332336
func (bc *BlockChain) GasLimit() uint64 {
333-
bc.mu.RLock()
334-
defer bc.mu.RUnlock()
335-
336-
return bc.currentBlock.GasLimit()
337+
return bc.CurrentBlock().GasLimit()
337338
}
338339

339340
// CurrentBlock retrieves the current head block of the canonical chain. The
340341
// block is retrieved from the blockchain's internal cache.
341342
func (bc *BlockChain) CurrentBlock() *types.Block {
342-
bc.mu.RLock()
343-
defer bc.mu.RUnlock()
344-
345-
return bc.currentBlock
343+
return bc.currentBlock.Load().(*types.Block)
346344
}
347345

348346
// CurrentFastBlock retrieves the current fast-sync head block of the canonical
349347
// chain. The block is retrieved from the blockchain's internal cache.
350348
func (bc *BlockChain) CurrentFastBlock() *types.Block {
351-
bc.mu.RLock()
352-
defer bc.mu.RUnlock()
353-
354-
return bc.currentFastBlock
349+
return bc.currentFastBlock.Load().(*types.Block)
355350
}
356351

357352
// SetProcessor sets the processor required for making state modifications.
@@ -416,10 +411,10 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
416411
}
417412
bc.genesisBlock = genesis
418413
bc.insert(bc.genesisBlock)
419-
bc.currentBlock = bc.genesisBlock
414+
bc.currentBlock.Store(bc.genesisBlock)
420415
bc.hc.SetGenesis(bc.genesisBlock.Header())
421416
bc.hc.SetCurrentHeader(bc.genesisBlock.Header())
422-
bc.currentFastBlock = bc.genesisBlock
417+
bc.currentFastBlock.Store(bc.genesisBlock)
423418

424419
return nil
425420
}
@@ -444,7 +439,7 @@ func (bc *BlockChain) repair(head **types.Block) error {
444439

445440
// Export writes the active chain to the given writer.
446441
func (bc *BlockChain) Export(w io.Writer) error {
447-
return bc.ExportN(w, uint64(0), bc.currentBlock.NumberU64())
442+
return bc.ExportN(w, uint64(0), bc.CurrentBlock().NumberU64())
448443
}
449444

450445
// ExportN writes a subset of the active chain to the given writer.
@@ -488,7 +483,7 @@ func (bc *BlockChain) insert(block *types.Block) {
488483
if err := WriteHeadBlockHash(bc.db, block.Hash()); err != nil {
489484
log.Crit("Failed to insert head block hash", "err", err)
490485
}
491-
bc.currentBlock = block
486+
bc.currentBlock.Store(block)
492487

493488
// If the block is better than our head or is on a different chain, force update heads
494489
if updateHeads {
@@ -497,7 +492,7 @@ func (bc *BlockChain) insert(block *types.Block) {
497492
if err := WriteHeadFastBlockHash(bc.db, block.Hash()); err != nil {
498493
log.Crit("Failed to insert head fast block hash", "err", err)
499494
}
500-
bc.currentFastBlock = block
495+
bc.currentFastBlock.Store(block)
501496
}
502497
}
503498

@@ -714,13 +709,15 @@ func (bc *BlockChain) Rollback(chain []common.Hash) {
714709
if currentHeader.Hash() == hash {
715710
bc.hc.SetCurrentHeader(bc.GetHeader(currentHeader.ParentHash, currentHeader.Number.Uint64()-1))
716711
}
717-
if bc.currentFastBlock.Hash() == hash {
718-
bc.currentFastBlock = bc.GetBlock(bc.currentFastBlock.ParentHash(), bc.currentFastBlock.NumberU64()-1)
719-
WriteHeadFastBlockHash(bc.db, bc.currentFastBlock.Hash())
712+
if currentFastBlock := bc.CurrentFastBlock(); currentFastBlock.Hash() == hash {
713+
newFastBlock := bc.GetBlock(currentFastBlock.ParentHash(), currentFastBlock.NumberU64()-1)
714+
bc.currentFastBlock.Store(newFastBlock)
715+
WriteHeadFastBlockHash(bc.db, newFastBlock.Hash())
720716
}
721-
if bc.currentBlock.Hash() == hash {
722-
bc.currentBlock = bc.GetBlock(bc.currentBlock.ParentHash(), bc.currentBlock.NumberU64()-1)
723-
WriteHeadBlockHash(bc.db, bc.currentBlock.Hash())
717+
if currentBlock := bc.CurrentBlock(); currentBlock.Hash() == hash {
718+
newBlock := bc.GetBlock(currentBlock.ParentHash(), currentBlock.NumberU64()-1)
719+
bc.currentBlock.Store(newBlock)
720+
WriteHeadBlockHash(bc.db, newBlock.Hash())
724721
}
725722
}
726723
}
@@ -829,11 +826,12 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
829826
bc.mu.Lock()
830827
head := blockChain[len(blockChain)-1]
831828
if td := bc.GetTd(head.Hash(), head.NumberU64()); td != nil { // Rewind may have occurred, skip in that case
832-
if bc.GetTd(bc.currentFastBlock.Hash(), bc.currentFastBlock.NumberU64()).Cmp(td) < 0 {
829+
currentFastBlock := bc.CurrentFastBlock()
830+
if bc.GetTd(currentFastBlock.Hash(), currentFastBlock.NumberU64()).Cmp(td) < 0 {
833831
if err := WriteHeadFastBlockHash(bc.db, head.Hash()); err != nil {
834832
log.Crit("Failed to update head fast block hash", "err", err)
835833
}
836-
bc.currentFastBlock = head
834+
bc.currentFastBlock.Store(head)
837835
}
838836
}
839837
bc.mu.Unlock()
@@ -880,7 +878,8 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
880878
bc.mu.Lock()
881879
defer bc.mu.Unlock()
882880

883-
localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
881+
currentBlock := bc.CurrentBlock()
882+
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
884883
externTd := new(big.Int).Add(block.Difficulty(), ptd)
885884

886885
// Irrelevant of the canonical status, write the block itself to the database
@@ -955,14 +954,15 @@ func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.
955954
// Second clause in the if statement reduces the vulnerability to selfish mining.
956955
// Please refer to http://www.cs.cornell.edu/~ie53/publications/btcProcFC.pdf
957956
reorg := externTd.Cmp(localTd) > 0
957+
currentBlock = bc.CurrentBlock()
958958
if !reorg && externTd.Cmp(localTd) == 0 {
959959
// Split same-difficulty blocks by number, then at random
960-
reorg = block.NumberU64() < bc.currentBlock.NumberU64() || (block.NumberU64() == bc.currentBlock.NumberU64() && mrand.Float64() < 0.5)
960+
reorg = block.NumberU64() < currentBlock.NumberU64() || (block.NumberU64() == currentBlock.NumberU64() && mrand.Float64() < 0.5)
961961
}
962962
if reorg {
963963
// Reorganise the chain if the parent is not the head block
964-
if block.ParentHash() != bc.currentBlock.Hash() {
965-
if err := bc.reorg(bc.currentBlock, block); err != nil {
964+
if block.ParentHash() != currentBlock.Hash() {
965+
if err := bc.reorg(currentBlock, block); err != nil {
966966
return NonStatTy, err
967967
}
968968
}
@@ -1091,7 +1091,8 @@ func (bc *BlockChain) insertChain(chain types.Blocks) (int, []interface{}, []*ty
10911091
case err == consensus.ErrPrunedAncestor:
10921092
// Block competing with the canonical chain, store in the db, but don't process
10931093
// until the competitor TD goes above the canonical TD
1094-
localTd := bc.GetTd(bc.currentBlock.Hash(), bc.currentBlock.NumberU64())
1094+
currentBlock := bc.CurrentBlock()
1095+
localTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
10951096
externTd := new(big.Int).Add(bc.GetTd(block.ParentHash(), block.NumberU64()-1), block.Difficulty())
10961097
if localTd.Cmp(externTd) > 0 {
10971098
if err = bc.WriteBlockWithoutState(block, externTd); err != nil {
@@ -1480,9 +1481,6 @@ func (bc *BlockChain) writeHeader(header *types.Header) error {
14801481
// CurrentHeader retrieves the current head header of the canonical chain. The
14811482
// header is retrieved from the HeaderChain's internal cache.
14821483
func (bc *BlockChain) CurrentHeader() *types.Header {
1483-
bc.mu.RLock()
1484-
defer bc.mu.RUnlock()
1485-
14861484
return bc.hc.CurrentHeader()
14871485
}
14881486

core/headerchain.go

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/ethereum/go-ethereum/log"
3333
"github.com/ethereum/go-ethereum/params"
3434
"github.com/hashicorp/golang-lru"
35+
"sync/atomic"
3536
)
3637

3738
const (
@@ -51,8 +52,8 @@ type HeaderChain struct {
5152
chainDb ethdb.Database
5253
genesisHeader *types.Header
5354

54-
currentHeader *types.Header // Current head of the header chain (may be above the block chain!)
55-
currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
55+
currentHeader atomic.Value // Current head of the header chain (may be above the block chain!)
56+
currentHeaderHash common.Hash // Hash of the current head of the header chain (prevent recomputing all the time)
5657

5758
headerCache *lru.Cache // Cache for the most recent block headers
5859
tdCache *lru.Cache // Cache for the most recent block total difficulties
@@ -95,13 +96,13 @@ func NewHeaderChain(chainDb ethdb.Database, config *params.ChainConfig, engine c
9596
return nil, ErrNoGenesis
9697
}
9798

98-
hc.currentHeader = hc.genesisHeader
99+
hc.currentHeader.Store(hc.genesisHeader)
99100
if head := GetHeadBlockHash(chainDb); head != (common.Hash{}) {
100101
if chead := hc.GetHeaderByHash(head); chead != nil {
101-
hc.currentHeader = chead
102+
hc.currentHeader.Store(chead)
102103
}
103104
}
104-
hc.currentHeaderHash = hc.currentHeader.Hash()
105+
hc.currentHeaderHash = hc.CurrentHeader().Hash()
105106

106107
return hc, nil
107108
}
@@ -139,7 +140,7 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
139140
if ptd == nil {
140141
return NonStatTy, consensus.ErrUnknownAncestor
141142
}
142-
localTd := hc.GetTd(hc.currentHeaderHash, hc.currentHeader.Number.Uint64())
143+
localTd := hc.GetTd(hc.currentHeaderHash, hc.CurrentHeader().Number.Uint64())
143144
externTd := new(big.Int).Add(header.Difficulty, ptd)
144145

145146
// Irrelevant of the canonical status, write the td and header to the database
@@ -181,7 +182,8 @@ func (hc *HeaderChain) WriteHeader(header *types.Header) (status WriteStatus, er
181182
if err := WriteHeadHeaderHash(hc.chainDb, hash); err != nil {
182183
log.Crit("Failed to insert head header hash", "err", err)
183184
}
184-
hc.currentHeaderHash, hc.currentHeader = hash, types.CopyHeader(header)
185+
hc.currentHeaderHash = hash
186+
hc.currentHeader.Store(types.CopyHeader(header))
185187

186188
status = CanonStatTy
187189
} else {
@@ -383,15 +385,15 @@ func (hc *HeaderChain) GetHeaderByNumber(number uint64) *types.Header {
383385
// CurrentHeader retrieves the current head header of the canonical chain. The
384386
// header is retrieved from the HeaderChain's internal cache.
385387
func (hc *HeaderChain) CurrentHeader() *types.Header {
386-
return hc.currentHeader
388+
return hc.currentHeader.Load().(*types.Header)
387389
}
388390

389391
// SetCurrentHeader sets the current head header of the canonical chain.
390392
func (hc *HeaderChain) SetCurrentHeader(head *types.Header) {
391393
if err := WriteHeadHeaderHash(hc.chainDb, head.Hash()); err != nil {
392394
log.Crit("Failed to insert head header hash", "err", err)
393395
}
394-
hc.currentHeader = head
396+
hc.currentHeader.Store(head)
395397
hc.currentHeaderHash = head.Hash()
396398
}
397399

@@ -403,19 +405,20 @@ type DeleteCallback func(common.Hash, uint64)
403405
// will be deleted and the new one set.
404406
func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
405407
height := uint64(0)
406-
if hc.currentHeader != nil {
407-
height = hc.currentHeader.Number.Uint64()
408+
409+
if hdr := hc.CurrentHeader(); hdr != nil {
410+
height = hdr.Number.Uint64()
408411
}
409412

410-
for hc.currentHeader != nil && hc.currentHeader.Number.Uint64() > head {
411-
hash := hc.currentHeader.Hash()
412-
num := hc.currentHeader.Number.Uint64()
413+
for hdr := hc.CurrentHeader(); hdr != nil && hdr.Number.Uint64() > head; hdr = hc.CurrentHeader() {
414+
hash := hdr.Hash()
415+
num := hdr.Number.Uint64()
413416
if delFn != nil {
414417
delFn(hash, num)
415418
}
416419
DeleteHeader(hc.chainDb, hash, num)
417420
DeleteTd(hc.chainDb, hash, num)
418-
hc.currentHeader = hc.GetHeader(hc.currentHeader.ParentHash, hc.currentHeader.Number.Uint64()-1)
421+
hc.currentHeader.Store(hc.GetHeader(hdr.ParentHash, hdr.Number.Uint64()-1))
419422
}
420423
// Roll back the canonical chain numbering
421424
for i := height; i > head; i-- {
@@ -426,10 +429,10 @@ func (hc *HeaderChain) SetHead(head uint64, delFn DeleteCallback) {
426429
hc.tdCache.Purge()
427430
hc.numberCache.Purge()
428431

429-
if hc.currentHeader == nil {
430-
hc.currentHeader = hc.genesisHeader
432+
if hc.CurrentHeader() == nil {
433+
hc.currentHeader.Store(hc.genesisHeader)
431434
}
432-
hc.currentHeaderHash = hc.currentHeader.Hash()
435+
hc.currentHeaderHash = hc.CurrentHeader().Hash()
433436

434437
if err := WriteHeadHeaderHash(hc.chainDb, hc.currentHeaderHash); err != nil {
435438
log.Crit("Failed to reset head header hash", "err", err)

0 commit comments

Comments
 (0)