forked from cerc-io/plugeth
Queued approach to delivering chain events
This commit is contained in:
parent
b72ca57775
commit
8d9be18b29
@ -19,11 +19,6 @@ var (
|
||||
jsonlogger = logger.NewJsonLogger()
|
||||
)
|
||||
|
||||
type ChainEvent struct {
|
||||
Block *types.Block
|
||||
Td *big.Int
|
||||
}
|
||||
|
||||
type StateQuery interface {
|
||||
GetAccount(addr []byte) *state.StateObject
|
||||
}
|
||||
@ -93,13 +88,16 @@ type ChainManager struct {
|
||||
|
||||
transState *state.StateDB
|
||||
txState *state.StateDB
|
||||
|
||||
quit chan struct{}
|
||||
}
|
||||
|
||||
func NewChainManager(db ethutil.Database, mux *event.TypeMux) *ChainManager {
|
||||
bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux}
|
||||
bc := &ChainManager{db: db, genesisBlock: GenesisBlock(db), eventMux: mux, quit: make(chan struct{})}
|
||||
bc.setLastBlock()
|
||||
bc.transState = bc.State().Copy()
|
||||
bc.txState = bc.State().Copy()
|
||||
go bc.update()
|
||||
|
||||
return bc
|
||||
}
|
||||
@ -388,16 +386,24 @@ func (self *ChainManager) CalcTotalDiff(block *types.Block) (*big.Int, error) {
|
||||
}
|
||||
|
||||
func (bc *ChainManager) Stop() {
|
||||
if bc.CurrentBlock != nil {
|
||||
chainlogger.Infoln("Stopped")
|
||||
}
|
||||
close(bc.quit)
|
||||
}
|
||||
|
||||
type queueEvent struct {
|
||||
queue []interface{}
|
||||
canonicalCount int
|
||||
sideCount int
|
||||
splitCount int
|
||||
}
|
||||
|
||||
func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||
self.tsmu.Lock()
|
||||
defer self.tsmu.Unlock()
|
||||
//self.tsmu.Lock()
|
||||
//defer self.tsmu.Unlock()
|
||||
|
||||
for _, block := range chain {
|
||||
// A queued approach to delivering events. This is generally faster than direct delivery and requires much less mutex acquiring.
|
||||
var queue = make([]interface{}, len(chain))
|
||||
var queueEvent = queueEvent{queue: queue}
|
||||
for i, block := range chain {
|
||||
// Call in to the block processor and check for errors. It's likely that if one block fails
|
||||
// all others will fail too (unless a known block is returned).
|
||||
td, err := self.processor.Process(block)
|
||||
@ -414,7 +420,6 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||
}
|
||||
block.Td = td
|
||||
|
||||
var canonical, split bool
|
||||
self.mu.Lock()
|
||||
cblock := self.currentBlock
|
||||
{
|
||||
@ -426,41 +431,75 @@ func (self *ChainManager) InsertChain(chain types.Blocks) error {
|
||||
if td.Cmp(self.td) > 0 {
|
||||
if block.Header().Number.Cmp(new(big.Int).Add(cblock.Header().Number, ethutil.Big1)) < 0 {
|
||||
chainlogger.Infof("Split detected. New head #%v (%x) TD=%v, was #%v (%x) TD=%v\n", block.Header().Number, block.Hash()[:4], td, cblock.Header().Number, cblock.Hash()[:4], self.td)
|
||||
split = true
|
||||
|
||||
queue[i] = ChainSplitEvent{block}
|
||||
queueEvent.splitCount++
|
||||
}
|
||||
|
||||
self.setTotalDifficulty(td)
|
||||
self.insert(block)
|
||||
|
||||
canonical = true
|
||||
/*
|
||||
jsonlogger.LogJson(&logger.EthChainNewHead{
|
||||
BlockHash: ethutil.Bytes2Hex(block.Hash()),
|
||||
BlockNumber: block.Number(),
|
||||
ChainHeadHash: ethutil.Bytes2Hex(cblock.Hash()),
|
||||
BlockPrevHash: ethutil.Bytes2Hex(block.ParentHash()),
|
||||
})
|
||||
*/
|
||||
|
||||
self.setTransState(state.New(block.Root(), self.db))
|
||||
queue[i] = ChainEvent{block}
|
||||
queueEvent.canonicalCount++
|
||||
} else {
|
||||
queue[i] = ChainSideEvent{block}
|
||||
queueEvent.sideCount++
|
||||
}
|
||||
}
|
||||
self.mu.Unlock()
|
||||
|
||||
if canonical {
|
||||
/*
|
||||
jsonlogger.LogJson(&logger.EthChainNewHead{
|
||||
BlockHash: ethutil.Bytes2Hex(block.Hash()),
|
||||
BlockNumber: block.Number(),
|
||||
ChainHeadHash: ethutil.Bytes2Hex(cblock.Hash()),
|
||||
BlockPrevHash: ethutil.Bytes2Hex(block.ParentHash()),
|
||||
})
|
||||
*/
|
||||
self.setTransState(state.New(block.Root(), self.db))
|
||||
self.eventMux.Post(ChainEvent{block, td})
|
||||
} else {
|
||||
//self.eventMux.
|
||||
}
|
||||
|
||||
if split {
|
||||
self.setTxState(state.New(block.Root(), self.db))
|
||||
self.eventMux.Post(ChainSplitEvent{block})
|
||||
}
|
||||
}
|
||||
|
||||
// XXX put this in a goroutine?
|
||||
go self.eventMux.Post(queueEvent)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (self *ChainManager) update() {
|
||||
events := self.eventMux.Subscribe(queueEvent{})
|
||||
|
||||
out:
|
||||
for {
|
||||
select {
|
||||
case ev := <-events.Chan():
|
||||
switch ev := ev.(type) {
|
||||
case queueEvent:
|
||||
for i, event := range ev.queue {
|
||||
switch event := event.(type) {
|
||||
case ChainEvent:
|
||||
// We need some control over the mining operation. Acquiring locks and waiting for the miner to create new block takes too long
|
||||
// and in most cases isn't even necessary.
|
||||
if i == ev.canonicalCount {
|
||||
self.eventMux.Post(ChainHeadEvent{event.Block})
|
||||
}
|
||||
case ChainSplitEvent:
|
||||
// On chain splits we need to reset the transaction state. We can't be sure whether the actual
|
||||
// state of the accounts are still valid.
|
||||
if i == ev.splitCount {
|
||||
self.setTxState(state.New(event.Block.Root(), self.db))
|
||||
}
|
||||
}
|
||||
|
||||
self.eventMux.Post(event)
|
||||
}
|
||||
}
|
||||
case <-self.quit:
|
||||
break out
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Satisfy state query interface
|
||||
func (self *ChainManager) GetAccount(addr []byte) *state.StateObject {
|
||||
return self.State().GetAccount(addr)
|
||||
|
@ -16,3 +16,13 @@ type NewMinedBlockEvent struct{ Block *types.Block }
|
||||
|
||||
// ChainSplit is posted when a new head is detected
|
||||
type ChainSplitEvent struct{ Block *types.Block }
|
||||
|
||||
type ChainEvent struct{ Block *types.Block }
|
||||
|
||||
type ChainSideEvent struct{ Block *types.Block }
|
||||
|
||||
type ChainHeadEvent struct{ Block *types.Block }
|
||||
|
||||
// Mining operation events
|
||||
type StartMining struct{}
|
||||
type TopMining struct{}
|
||||
|
@ -30,6 +30,7 @@ func New(coinbase []byte, eth core.Backend, pow pow.PoW, minerThreads int) *Mine
|
||||
pow: pow,
|
||||
}
|
||||
|
||||
minerThreads = 1
|
||||
for i := 0; i < minerThreads; i++ {
|
||||
miner.worker.register(NewCpuMiner(i, miner.pow))
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ func (self *worker) register(agent Agent) {
|
||||
}
|
||||
|
||||
func (self *worker) update() {
|
||||
events := self.mux.Subscribe(core.ChainEvent{}, core.NewMinedBlockEvent{})
|
||||
events := self.mux.Subscribe(core.ChainHeadEvent{}, core.NewMinedBlockEvent{})
|
||||
|
||||
timer := time.NewTicker(2 * time.Second)
|
||||
|
||||
@ -125,7 +125,7 @@ out:
|
||||
select {
|
||||
case event := <-events.Chan():
|
||||
switch ev := event.(type) {
|
||||
case core.ChainEvent:
|
||||
case core.ChainHeadEvent:
|
||||
if self.current.block != ev.Block {
|
||||
self.commitNewWork()
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user