diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go index fbde9c6c..c0013a11 100644 --- a/eth/downloader/downloader.go +++ b/eth/downloader/downloader.go @@ -143,6 +143,8 @@ type Downloader struct { quitCh chan struct{} // Quit channel to signal termination quitLock sync.RWMutex // Lock to prevent double closes + downloads sync.WaitGroup // Keeps track of the currently active downloads + // Testing hooks syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run bodyFetchHook func([]*types.Header) // Method to call upon starting a block body fetch @@ -403,7 +405,9 @@ func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode // specified peer and head hash. func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.Int) (err error) { d.mux.Post(StartEvent{}) + d.downloads.Add(1) defer func() { + d.downloads.Done() // reset on error if err != nil { d.mux.Post(FailedEvent{err}) @@ -471,14 +475,22 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I } else if d.mode == FullSync { fetchers = append(fetchers, d.processFullSyncContent) } - return d.spawnSync(fetchers) + return d.spawnSync(errCancelHeaderFetch, fetchers) } // spawnSync runs d.process and all given fetcher functions to completion in // separate goroutines, returning the first error that appears. -func (d *Downloader) spawnSync(fetchers []func() error) error { +func (d *Downloader) spawnSync(errCancel error, fetchers []func() error) error { + d.cancelLock.Lock() + select { + case <-d.cancelCh: + d.cancelLock.Unlock() + return errCancel + default: + } errc := make(chan error, len(fetchers)) d.cancelWg.Add(len(fetchers)) + d.cancelLock.Unlock() for _, fn := range fetchers { fn := fn go func() { defer d.cancelWg.Done(); errc <- fn() }() @@ -539,6 +551,10 @@ func (d *Downloader) Terminate() { // Cancel any pending download requests d.Cancel() + + // Wait, so external dependencies aren't destroyed + // until the download processing is done. + d.downloads.Wait() } // fetchHeight retrieves the head header of the remote peer to aid in estimating diff --git a/eth/handler.go b/eth/handler.go index f89f68c9..5522b0d9 100644 --- a/eth/handler.go +++ b/eth/handler.go @@ -230,6 +230,9 @@ func (pm *ProtocolManager) Stop() { // Quit fetcher, txsyncLoop. close(pm.quitSync) + // Stop downloader and make sure that all the running downloads are complete. + pm.downloader.Terminate() + // 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 diff --git a/eth/sync.go b/eth/sync.go index e49e4008..4367434a 100644 --- a/eth/sync.go +++ b/eth/sync.go @@ -135,7 +135,6 @@ func (pm *ProtocolManager) syncer() { // Start and ensure cleanup of sync mechanisms pm.fetcher.Start() defer pm.fetcher.Stop() - defer pm.downloader.Terminate() // Wait for different events to fire synchronisation operations forceSync := time.NewTicker(forceSyncCycle) diff --git a/les/backend.go b/les/backend.go index 00025ba6..38c36da6 100644 --- a/les/backend.go +++ b/les/backend.go @@ -20,7 +20,6 @@ package les import ( "fmt" "sync" - "time" "github.com/ethereum/go-ethereum/accounts" "github.com/ethereum/go-ethereum/common" @@ -253,7 +252,6 @@ func (s *LightEthereum) Stop() error { s.eventMux.Stop() - time.Sleep(time.Millisecond * 200) s.chainDb.Close() close(s.shutdownChan) diff --git a/les/handler.go b/les/handler.go index ca40eaab..cc15d68c 100644 --- a/les/handler.go +++ b/les/handler.go @@ -194,6 +194,9 @@ func (pm *ProtocolManager) Stop() { pm.clientPool.stop() } + // Stop downloader and make sure that all the running downloads are complete. + pm.downloader.Terminate() + // 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 diff --git a/les/sync.go b/les/sync.go index 1ac64558..eb155377 100644 --- a/les/sync.go +++ b/les/sync.go @@ -31,7 +31,6 @@ func (pm *ProtocolManager) syncer() { // Start and ensure cleanup of sync mechanisms //pm.fetcher.Start() //defer pm.fetcher.Stop() - defer pm.downloader.Terminate() // Wait for different events to fire synchronisation operations //forceSync := time.Tick(forceSyncCycle)