Merge pull request #2523 from fjl/shutdown
core, eth, miner: improve shutdown synchronisation
This commit is contained in:
commit
756b62988c
|
@ -60,7 +60,6 @@ type stateFn func() (*state.StateDB, error)
|
||||||
// two states over time as they are received and processed.
|
// two states over time as they are received and processed.
|
||||||
type TxPool struct {
|
type TxPool struct {
|
||||||
config *ChainConfig
|
config *ChainConfig
|
||||||
quit chan bool // Quitting channel
|
|
||||||
currentState stateFn // The state function which will allow us to do some pre checks
|
currentState stateFn // The state function which will allow us to do some pre checks
|
||||||
pendingState *state.ManagedState
|
pendingState *state.ManagedState
|
||||||
gasLimit func() *big.Int // The current gas limit function callback
|
gasLimit func() *big.Int // The current gas limit function callback
|
||||||
|
@ -72,6 +71,8 @@ type TxPool struct {
|
||||||
pending map[common.Hash]*types.Transaction // processable transactions
|
pending map[common.Hash]*types.Transaction // processable transactions
|
||||||
queue map[common.Address]map[common.Hash]*types.Transaction
|
queue map[common.Address]map[common.Hash]*types.Transaction
|
||||||
|
|
||||||
|
wg sync.WaitGroup // for shutdown sync
|
||||||
|
|
||||||
homestead bool
|
homestead bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +81,6 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat
|
||||||
config: config,
|
config: config,
|
||||||
pending: make(map[common.Hash]*types.Transaction),
|
pending: make(map[common.Hash]*types.Transaction),
|
||||||
queue: make(map[common.Address]map[common.Hash]*types.Transaction),
|
queue: make(map[common.Address]map[common.Hash]*types.Transaction),
|
||||||
quit: make(chan bool),
|
|
||||||
eventMux: eventMux,
|
eventMux: eventMux,
|
||||||
currentState: currentStateFn,
|
currentState: currentStateFn,
|
||||||
gasLimit: gasLimitFn,
|
gasLimit: gasLimitFn,
|
||||||
|
@ -90,12 +90,15 @@ func NewTxPool(config *ChainConfig, eventMux *event.TypeMux, currentStateFn stat
|
||||||
events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}),
|
events: eventMux.Subscribe(ChainHeadEvent{}, GasPriceChanged{}, RemovedTransactionEvent{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pool.wg.Add(1)
|
||||||
go pool.eventLoop()
|
go pool.eventLoop()
|
||||||
|
|
||||||
return pool
|
return pool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *TxPool) eventLoop() {
|
func (pool *TxPool) eventLoop() {
|
||||||
|
defer pool.wg.Done()
|
||||||
|
|
||||||
// Track chain events. When a chain events occurs (new chain canon block)
|
// Track chain events. When a chain events occurs (new chain canon block)
|
||||||
// we need to know the new state. The new state will help us determine
|
// we need to know the new state. The new state will help us determine
|
||||||
// the nonces in the managed state
|
// the nonces in the managed state
|
||||||
|
@ -155,8 +158,8 @@ func (pool *TxPool) resetState() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pool *TxPool) Stop() {
|
func (pool *TxPool) Stop() {
|
||||||
close(pool.quit)
|
|
||||||
pool.events.Unsubscribe()
|
pool.events.Unsubscribe()
|
||||||
|
pool.wg.Wait()
|
||||||
glog.V(logger.Info).Infoln("Transaction pool stopped")
|
glog.V(logger.Info).Infoln("Transaction pool stopped")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -416,6 +416,7 @@ func (s *Ethereum) Stop() error {
|
||||||
s.blockchain.Stop()
|
s.blockchain.Stop()
|
||||||
s.protocolManager.Stop()
|
s.protocolManager.Stop()
|
||||||
s.txPool.Stop()
|
s.txPool.Stop()
|
||||||
|
s.miner.Stop()
|
||||||
s.eventMux.Stop()
|
s.eventMux.Stop()
|
||||||
|
|
||||||
s.StopAutoDAG()
|
s.StopAutoDAG()
|
||||||
|
|
|
@ -77,11 +77,11 @@ type ProtocolManager struct {
|
||||||
newPeerCh chan *peer
|
newPeerCh chan *peer
|
||||||
txsyncCh chan *txsync
|
txsyncCh chan *txsync
|
||||||
quitSync chan struct{}
|
quitSync chan struct{}
|
||||||
|
noMorePeers chan struct{}
|
||||||
|
|
||||||
// wait group is used for graceful shutdowns during downloading
|
// wait group is used for graceful shutdowns during downloading
|
||||||
// and processing
|
// and processing
|
||||||
wg sync.WaitGroup
|
wg sync.WaitGroup
|
||||||
quit bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
// NewProtocolManager returns a new ethereum sub protocol manager. The Ethereum sub protocol manages peers capable
|
||||||
|
@ -101,7 +101,8 @@ func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int,
|
||||||
blockchain: blockchain,
|
blockchain: blockchain,
|
||||||
chaindb: chaindb,
|
chaindb: chaindb,
|
||||||
peers: newPeerSet(),
|
peers: newPeerSet(),
|
||||||
newPeerCh: make(chan *peer, 1),
|
newPeerCh: make(chan *peer),
|
||||||
|
noMorePeers: make(chan struct{}),
|
||||||
txsyncCh: make(chan *txsync),
|
txsyncCh: make(chan *txsync),
|
||||||
quitSync: make(chan struct{}),
|
quitSync: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
@ -120,8 +121,14 @@ func NewProtocolManager(config *core.ChainConfig, fastSync bool, networkId int,
|
||||||
Length: ProtocolLengths[i],
|
Length: ProtocolLengths[i],
|
||||||
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
Run: func(p *p2p.Peer, rw p2p.MsgReadWriter) error {
|
||||||
peer := manager.newPeer(int(version), p, rw)
|
peer := manager.newPeer(int(version), p, rw)
|
||||||
manager.newPeerCh <- peer
|
select {
|
||||||
|
case manager.newPeerCh <- peer:
|
||||||
|
manager.wg.Add(1)
|
||||||
|
defer manager.wg.Done()
|
||||||
return manager.handle(peer)
|
return manager.handle(peer)
|
||||||
|
case <-manager.quitSync:
|
||||||
|
return p2p.DiscQuitting
|
||||||
|
}
|
||||||
},
|
},
|
||||||
NodeInfo: func() interface{} {
|
NodeInfo: func() interface{} {
|
||||||
return manager.NodeInfo()
|
return manager.NodeInfo()
|
||||||
|
@ -187,16 +194,25 @@ func (pm *ProtocolManager) Start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProtocolManager) Stop() {
|
func (pm *ProtocolManager) Stop() {
|
||||||
// Showing a log message. During download / process this could actually
|
|
||||||
// take between 5 to 10 seconds and therefor feedback is required.
|
|
||||||
glog.V(logger.Info).Infoln("Stopping ethereum protocol handler...")
|
glog.V(logger.Info).Infoln("Stopping ethereum protocol handler...")
|
||||||
|
|
||||||
pm.quit = true
|
|
||||||
pm.txSub.Unsubscribe() // quits txBroadcastLoop
|
pm.txSub.Unsubscribe() // quits txBroadcastLoop
|
||||||
pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop
|
pm.minedBlockSub.Unsubscribe() // quits blockBroadcastLoop
|
||||||
close(pm.quitSync) // quits syncer, fetcher, txsyncLoop
|
|
||||||
|
|
||||||
// Wait for any process action
|
// Quit the sync loop.
|
||||||
|
// After this send has completed, no new peers will be accepted.
|
||||||
|
pm.noMorePeers <- struct{}{}
|
||||||
|
|
||||||
|
// Quit fetcher, txsyncLoop.
|
||||||
|
close(pm.quitSync)
|
||||||
|
|
||||||
|
// Disconnect existing sessions.
|
||||||
|
// This also closes the gate for any new registrations on the peer set.
|
||||||
|
// sessions which are already established but not added to pm.peers yet
|
||||||
|
// will exit when they try to register.
|
||||||
|
pm.peers.Close()
|
||||||
|
|
||||||
|
// Wait for all peer handler goroutines and the loops to come down.
|
||||||
pm.wg.Wait()
|
pm.wg.Wait()
|
||||||
|
|
||||||
glog.V(logger.Info).Infoln("Ethereum protocol handler stopped")
|
glog.V(logger.Info).Infoln("Ethereum protocol handler stopped")
|
||||||
|
|
|
@ -140,14 +140,14 @@ func newTestPeer(name string, version int, pm *ProtocolManager, shake bool) (*te
|
||||||
// Start the peer on a new thread
|
// Start the peer on a new thread
|
||||||
errc := make(chan error, 1)
|
errc := make(chan error, 1)
|
||||||
go func() {
|
go func() {
|
||||||
pm.newPeerCh <- peer
|
select {
|
||||||
|
case pm.newPeerCh <- peer:
|
||||||
errc <- pm.handle(peer)
|
errc <- pm.handle(peer)
|
||||||
}()
|
case <-pm.quitSync:
|
||||||
tp := &testPeer{
|
errc <- p2p.DiscQuitting
|
||||||
app: app,
|
|
||||||
net: net,
|
|
||||||
peer: peer,
|
|
||||||
}
|
}
|
||||||
|
}()
|
||||||
|
tp := &testPeer{app: app, net: net, peer: peer}
|
||||||
// Execute any implicitly requested handshakes and return
|
// Execute any implicitly requested handshakes and return
|
||||||
if shake {
|
if shake {
|
||||||
td, head, genesis := pm.blockchain.Status()
|
td, head, genesis := pm.blockchain.Status()
|
||||||
|
|
17
eth/peer.go
17
eth/peer.go
|
@ -34,6 +34,7 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
errClosed = errors.New("peer set is closed")
|
||||||
errAlreadyRegistered = errors.New("peer is already registered")
|
errAlreadyRegistered = errors.New("peer is already registered")
|
||||||
errNotRegistered = errors.New("peer is not registered")
|
errNotRegistered = errors.New("peer is not registered")
|
||||||
)
|
)
|
||||||
|
@ -353,6 +354,7 @@ func (p *peer) String() string {
|
||||||
type peerSet struct {
|
type peerSet struct {
|
||||||
peers map[string]*peer
|
peers map[string]*peer
|
||||||
lock sync.RWMutex
|
lock sync.RWMutex
|
||||||
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// newPeerSet creates a new peer set to track the active participants.
|
// newPeerSet creates a new peer set to track the active participants.
|
||||||
|
@ -368,6 +370,9 @@ func (ps *peerSet) Register(p *peer) error {
|
||||||
ps.lock.Lock()
|
ps.lock.Lock()
|
||||||
defer ps.lock.Unlock()
|
defer ps.lock.Unlock()
|
||||||
|
|
||||||
|
if ps.closed {
|
||||||
|
return errClosed
|
||||||
|
}
|
||||||
if _, ok := ps.peers[p.id]; ok {
|
if _, ok := ps.peers[p.id]; ok {
|
||||||
return errAlreadyRegistered
|
return errAlreadyRegistered
|
||||||
}
|
}
|
||||||
|
@ -450,3 +455,15 @@ func (ps *peerSet) BestPeer() *peer {
|
||||||
}
|
}
|
||||||
return bestPeer
|
return bestPeer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Close disconnects all peers.
|
||||||
|
// No new peers can be registered after Close has returned.
|
||||||
|
func (ps *peerSet) Close() {
|
||||||
|
ps.lock.Lock()
|
||||||
|
defer ps.lock.Unlock()
|
||||||
|
|
||||||
|
for _, p := range ps.peers {
|
||||||
|
p.Disconnect(p2p.DiscQuitting)
|
||||||
|
}
|
||||||
|
ps.closed = true
|
||||||
|
}
|
||||||
|
|
|
@ -148,7 +148,7 @@ func (pm *ProtocolManager) syncer() {
|
||||||
// Force a sync even if not enough peers are present
|
// Force a sync even if not enough peers are present
|
||||||
go pm.synchronise(pm.peers.BestPeer())
|
go pm.synchronise(pm.peers.BestPeer())
|
||||||
|
|
||||||
case <-pm.quitSync:
|
case <-pm.noMorePeers:
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -94,10 +94,13 @@ type worker struct {
|
||||||
|
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
|
|
||||||
|
// update loop
|
||||||
|
mux *event.TypeMux
|
||||||
|
events event.Subscription
|
||||||
|
wg sync.WaitGroup
|
||||||
|
|
||||||
agents map[Agent]struct{}
|
agents map[Agent]struct{}
|
||||||
recv chan *Result
|
recv chan *Result
|
||||||
mux *event.TypeMux
|
|
||||||
quit chan struct{}
|
|
||||||
pow pow.PoW
|
pow pow.PoW
|
||||||
|
|
||||||
eth core.Backend
|
eth core.Backend
|
||||||
|
@ -138,13 +141,14 @@ func newWorker(config *core.ChainConfig, coinbase common.Address, eth core.Backe
|
||||||
possibleUncles: make(map[common.Hash]*types.Block),
|
possibleUncles: make(map[common.Hash]*types.Block),
|
||||||
coinbase: coinbase,
|
coinbase: coinbase,
|
||||||
txQueue: make(map[common.Hash]*types.Transaction),
|
txQueue: make(map[common.Hash]*types.Transaction),
|
||||||
quit: make(chan struct{}),
|
|
||||||
agents: make(map[Agent]struct{}),
|
agents: make(map[Agent]struct{}),
|
||||||
fullValidation: false,
|
fullValidation: false,
|
||||||
}
|
}
|
||||||
|
worker.events = worker.mux.Subscribe(core.ChainHeadEvent{}, core.ChainSideEvent{}, core.TxPreEvent{})
|
||||||
|
worker.wg.Add(1)
|
||||||
go worker.update()
|
go worker.update()
|
||||||
go worker.wait()
|
|
||||||
|
|
||||||
|
go worker.wait()
|
||||||
worker.commitNewWork()
|
worker.commitNewWork()
|
||||||
|
|
||||||
return worker
|
return worker
|
||||||
|
@ -184,9 +188,12 @@ func (self *worker) start() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *worker) stop() {
|
func (self *worker) stop() {
|
||||||
|
// Quit update.
|
||||||
|
self.events.Unsubscribe()
|
||||||
|
self.wg.Wait()
|
||||||
|
|
||||||
self.mu.Lock()
|
self.mu.Lock()
|
||||||
defer self.mu.Unlock()
|
defer self.mu.Unlock()
|
||||||
|
|
||||||
if atomic.LoadInt32(&self.mining) == 1 {
|
if atomic.LoadInt32(&self.mining) == 1 {
|
||||||
// Stop all agents.
|
// Stop all agents.
|
||||||
for agent := range self.agents {
|
for agent := range self.agents {
|
||||||
|
@ -217,18 +224,8 @@ func (self *worker) unregister(agent Agent) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *worker) update() {
|
func (self *worker) update() {
|
||||||
eventSub := self.mux.Subscribe(core.ChainHeadEvent{}, core.ChainSideEvent{}, core.TxPreEvent{})
|
defer self.wg.Done()
|
||||||
defer eventSub.Unsubscribe()
|
for event := range self.events.Chan() {
|
||||||
|
|
||||||
eventCh := eventSub.Chan()
|
|
||||||
for {
|
|
||||||
select {
|
|
||||||
case event, ok := <-eventCh:
|
|
||||||
if !ok {
|
|
||||||
// Event subscription closed, set the channel to nil to stop spinning
|
|
||||||
eventCh = nil
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
// A real event arrived, process interesting content
|
// A real event arrived, process interesting content
|
||||||
switch ev := event.Data.(type) {
|
switch ev := event.Data.(type) {
|
||||||
case core.ChainHeadEvent:
|
case core.ChainHeadEvent:
|
||||||
|
@ -245,9 +242,6 @@ func (self *worker) update() {
|
||||||
self.currentMu.Unlock()
|
self.currentMu.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case <-self.quit:
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue