miner: subscribe downloader event by feed

This commit is contained in:
rjl493456442 2018-05-08 16:26:55 +08:00
parent d0b3667037
commit 13cb67effa
2 changed files with 42 additions and 16 deletions

View file

@ -16,10 +16,10 @@
package downloader package downloader
// StartEvent is posted when downloader start to synchronization // StartEvent is posted when downloader starts synchronization
type StartEvent struct{} type StartEvent struct{}
// FinishEvent is posted when downloader finish synchronization. // FinishEvent is posted when downloader finishes synchronization.
// If synchronization is not successful, a relevant error will be returned. // If synchronization is not successful, a relevant error will be returned.
type FinishEvent struct { type FinishEvent struct {
Err error Err error

View file

@ -40,12 +40,11 @@ type Backend interface {
BlockChain() *core.BlockChain BlockChain() *core.BlockChain
TxPool() *core.TxPool TxPool() *core.TxPool
ChainDb() ethdb.Database ChainDb() ethdb.Database
Downloader() *downloader.Downloader
} }
// Miner creates blocks and searches for proof-of-work values. // Miner creates blocks and searches for proof-of-work values.
type Miner struct { type Miner struct {
mux *event.TypeMux
worker *worker worker *worker
coinbase common.Address coinbase common.Address
@ -55,16 +54,34 @@ type Miner struct {
canStart int32 // can start indicates whether we can start the mining operation canStart int32 // can start indicates whether we can start the mining operation
shouldStart int32 // should start indicates whether we should start after sync shouldStart int32 // should start indicates whether we should start after sync
// Channels
startCh chan downloader.StartEvent
finishCh chan downloader.FinishEvent
// Subscriptions
startSub event.Subscription
finishSub event.Subscription
} }
func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine) *Miner { func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine) *Miner {
miner := &Miner{ miner := &Miner{
eth: eth, eth: eth,
mux: mux,
engine: engine, engine: engine,
worker: newWorker(config, engine, common.Address{}, eth, mux), worker: newWorker(config, engine, common.Address{}, eth, mux),
canStart: 1, canStart: 1,
startCh: make(chan downloader.StartEvent, 10),
finishCh: make(chan downloader.FinishEvent, 10),
} }
// Subscribe downloader events and make sure all the
// subscriptions are not empty.
miner.startSub = eth.Downloader().SubscribeStartEvent(miner.startCh)
miner.finishSub = eth.Downloader().SubscribeFinishEvent(miner.finishCh)
if miner.startSub == nil || miner.finishSub == nil {
log.Crit("Subscribe downloader events failed")
}
miner.Register(NewCpuAgent(eth.BlockChain(), engine)) miner.Register(NewCpuAgent(eth.BlockChain(), engine))
go miner.update() go miner.update()
@ -72,22 +89,28 @@ func New(eth Backend, config *params.ChainConfig, mux *event.TypeMux, engine con
} }
// update keeps track of the downloader events. Please be aware that this is a one shot type of update loop. // update keeps track of the downloader events. Please be aware that this is a one shot type of update loop.
// It's entered once and as soon as `Done` or `Failed` has been broadcasted the events are unregistered and // It's entered once and as soon as finish event has been broadcasted the events are unregistered and
// the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks // the loop is exited. This to prevent a major security vuln where external parties can DOS you with blocks
// and halt your mining operation for as long as the DOS continues. // and halt your mining operation for as long as the DOS continues.
func (self *Miner) update() { func (self *Miner) update() {
events := self.mux.Subscribe(downloader.StartEvent{}, downloader.FinishEvent{}) defer func() {
out: // Unsubscribe all downloader events
for ev := range events.Chan() { self.startSub.Unsubscribe()
switch ev.Data.(type) { self.finishSub.Unsubscribe()
case downloader.StartEvent: }()
for {
select {
case <-self.startCh:
atomic.StoreInt32(&self.canStart, 0) atomic.StoreInt32(&self.canStart, 0)
if self.Mining() { if self.Mining() {
self.Stop() self.Stop()
atomic.StoreInt32(&self.shouldStart, 1) atomic.StoreInt32(&self.shouldStart, 1)
log.Info("Mining aborted due to sync") log.Info("Mining aborted due to sync")
} }
case downloader.FinishEvent:
case <-self.finishCh:
// We only interested the downloader finish event once.
shouldStart := atomic.LoadInt32(&self.shouldStart) == 1 shouldStart := atomic.LoadInt32(&self.shouldStart) == 1
atomic.StoreInt32(&self.canStart, 1) atomic.StoreInt32(&self.canStart, 1)
@ -95,10 +118,13 @@ out:
if shouldStart { if shouldStart {
self.Start(self.coinbase) self.Start(self.coinbase)
} }
// unsubscribe. we're only interested in this event once // Stop immediately and ignore all further pending events
events.Unsubscribe() return
// stop immediately and ignore all further pending events
break out case <-self.startSub.Err():
return
case <-self.finishSub.Err():
return
} }
} }
} }