diff --git a/eth/backend.go b/eth/backend.go index 793d3b81f..9bfa844c7 100644 --- a/eth/backend.go +++ b/eth/backend.go @@ -554,7 +554,7 @@ func (s *Ethereum) Stop() error { s.bloomIndexer.Close() close(s.closeBloomHandler) s.txPool.Stop() - s.miner.Stop() + s.miner.Close() s.blockchain.Stop() s.engine.Close() rawdb.PopUncleanShutdownMarker(s.chainDb) diff --git a/miner/miner.go b/miner/miner.go index a4a01b9f4..1c33b3bd2 100644 --- a/miner/miner.go +++ b/miner/miner.go @@ -20,6 +20,7 @@ package miner import ( "fmt" "math/big" + "sync" "time" "github.com/ethereum/go-ethereum/common" @@ -63,6 +64,8 @@ type Miner struct { exitCh chan struct{} startCh chan common.Address stopCh chan struct{} + + wg sync.WaitGroup } func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(block *types.Block) bool) *Miner { @@ -75,8 +78,8 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even stopCh: make(chan struct{}), worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true), } + miner.wg.Add(1) go miner.update() - return miner } @@ -85,6 +88,8 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even // 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. func (miner *Miner) update() { + defer miner.wg.Done() + events := miner.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{}) defer func() { if !events.Closed() { @@ -154,6 +159,7 @@ func (miner *Miner) Stop() { func (miner *Miner) Close() { close(miner.exitCh) + miner.wg.Wait() } func (miner *Miner) Mining() bool { diff --git a/miner/worker.go b/miner/worker.go index 8bdb1eff7..f4c901e18 100644 --- a/miner/worker.go +++ b/miner/worker.go @@ -150,6 +150,8 @@ type worker struct { resubmitIntervalCh chan time.Duration resubmitAdjustCh chan *intervalAdjust + wg sync.WaitGroup + current *environment // An environment for current running cycle. localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks. remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks. @@ -225,6 +227,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus recommit = minRecommitInterval } + worker.wg.Add(4) go worker.mainLoop() go worker.newWorkLoop(recommit) go worker.resultLoop() @@ -323,6 +326,7 @@ func (w *worker) close() { } atomic.StoreInt32(&w.running, 0) close(w.exitCh) + w.wg.Wait() } // recalcRecommit recalculates the resubmitting interval upon feedback. @@ -349,6 +353,7 @@ func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) t // newWorkLoop is a standalone goroutine to submit new mining work upon received events. func (w *worker) newWorkLoop(recommit time.Duration) { + defer w.wg.Done() var ( interrupt *int32 minRecommit = recommit // minimal resubmit interval specified by user. @@ -446,6 +451,7 @@ func (w *worker) newWorkLoop(recommit time.Duration) { // mainLoop is a standalone goroutine to regenerate the sealing task based on the received event. func (w *worker) mainLoop() { + defer w.wg.Done() defer w.txsSub.Unsubscribe() defer w.chainHeadSub.Unsubscribe() defer w.chainSideSub.Unsubscribe() @@ -548,6 +554,7 @@ func (w *worker) mainLoop() { // taskLoop is a standalone goroutine to fetch sealing task from the generator and // push them to consensus engine. func (w *worker) taskLoop() { + defer w.wg.Done() var ( stopCh chan struct{} prev common.Hash @@ -595,6 +602,7 @@ func (w *worker) taskLoop() { // resultLoop is a standalone goroutine to handle sealing result submitting // and flush relative data to the database. func (w *worker) resultLoop() { + defer w.wg.Done() for { select { case block := <-w.resultCh: