mirror of
https://github.com/ethereum/go-ethereum.git
synced 2026-08-19 10:22:23 +00:00
p2p/discover: ensure no goroutines remain after shutdown
This commit is contained in:
parent
5adb6d0e89
commit
91025b7200
2 changed files with 18 additions and 8 deletions
|
|
@ -180,6 +180,10 @@ func (tab *Table) ReadRandomNodes(buf []*enode.Node) (n int) {
|
||||||
|
|
||||||
// Close terminates the network listener and flushes the node database.
|
// Close terminates the network listener and flushes the node database.
|
||||||
func (tab *Table) Close() {
|
func (tab *Table) Close() {
|
||||||
|
if tab.net != nil {
|
||||||
|
tab.net.close()
|
||||||
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-tab.closed:
|
case <-tab.closed:
|
||||||
// already closed.
|
// already closed.
|
||||||
|
|
@ -337,8 +341,8 @@ func (tab *Table) loop() {
|
||||||
revalidate = time.NewTimer(tab.nextRevalidateTime())
|
revalidate = time.NewTimer(tab.nextRevalidateTime())
|
||||||
refresh = time.NewTicker(refreshInterval)
|
refresh = time.NewTicker(refreshInterval)
|
||||||
copyNodes = time.NewTicker(copyNodesInterval)
|
copyNodes = time.NewTicker(copyNodesInterval)
|
||||||
revalidateDone = make(chan struct{})
|
|
||||||
refreshDone = make(chan struct{}) // where doRefresh reports completion
|
refreshDone = make(chan struct{}) // where doRefresh reports completion
|
||||||
|
revalidateDone chan struct{} // where doRevalidate reports completion
|
||||||
waiting = []chan struct{}{tab.initDone} // holds waiting callers while doRefresh runs
|
waiting = []chan struct{}{tab.initDone} // holds waiting callers while doRefresh runs
|
||||||
)
|
)
|
||||||
defer refresh.Stop()
|
defer refresh.Stop()
|
||||||
|
|
@ -369,9 +373,11 @@ loop:
|
||||||
}
|
}
|
||||||
waiting, refreshDone = nil, nil
|
waiting, refreshDone = nil, nil
|
||||||
case <-revalidate.C:
|
case <-revalidate.C:
|
||||||
|
revalidateDone = make(chan struct{})
|
||||||
go tab.doRevalidate(revalidateDone)
|
go tab.doRevalidate(revalidateDone)
|
||||||
case <-revalidateDone:
|
case <-revalidateDone:
|
||||||
revalidate.Reset(tab.nextRevalidateTime())
|
revalidate.Reset(tab.nextRevalidateTime())
|
||||||
|
revalidateDone = nil
|
||||||
case <-copyNodes.C:
|
case <-copyNodes.C:
|
||||||
go tab.copyLiveNodes()
|
go tab.copyLiveNodes()
|
||||||
case <-tab.closeReq:
|
case <-tab.closeReq:
|
||||||
|
|
@ -379,15 +385,15 @@ loop:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if tab.net != nil {
|
|
||||||
tab.net.close()
|
|
||||||
}
|
|
||||||
if refreshDone != nil {
|
if refreshDone != nil {
|
||||||
<-refreshDone
|
<-refreshDone
|
||||||
}
|
}
|
||||||
for _, ch := range waiting {
|
for _, ch := range waiting {
|
||||||
close(ch)
|
close(ch)
|
||||||
}
|
}
|
||||||
|
if revalidateDone != nil {
|
||||||
|
<-revalidateDone
|
||||||
|
}
|
||||||
close(tab.closed)
|
close(tab.closed)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,12 +23,12 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/crypto"
|
"github.com/ethereum/go-ethereum/crypto"
|
||||||
"github.com/ethereum/go-ethereum/log"
|
"github.com/ethereum/go-ethereum/log"
|
||||||
"github.com/ethereum/go-ethereum/p2p/enode"
|
"github.com/ethereum/go-ethereum/p2p/enode"
|
||||||
"github.com/ethereum/go-ethereum/p2p/nat"
|
|
||||||
"github.com/ethereum/go-ethereum/p2p/netutil"
|
"github.com/ethereum/go-ethereum/p2p/netutil"
|
||||||
"github.com/ethereum/go-ethereum/rlp"
|
"github.com/ethereum/go-ethereum/rlp"
|
||||||
)
|
)
|
||||||
|
|
@ -175,7 +175,7 @@ type udp struct {
|
||||||
localNode *enode.LocalNode
|
localNode *enode.LocalNode
|
||||||
db *enode.DB
|
db *enode.DB
|
||||||
tab *Table
|
tab *Table
|
||||||
nat nat.Interface
|
wg sync.WaitGroup
|
||||||
|
|
||||||
addpending chan *pending
|
addpending chan *pending
|
||||||
gotreply chan reply
|
gotreply chan reply
|
||||||
|
|
@ -262,6 +262,7 @@ func newUDP(c conn, ln *enode.LocalNode, cfg Config) (*Table, *udp, error) {
|
||||||
}
|
}
|
||||||
udp.tab = tab
|
udp.tab = tab
|
||||||
|
|
||||||
|
udp.wg.Add(2)
|
||||||
go udp.loop()
|
go udp.loop()
|
||||||
go udp.readLoop(cfg.Unhandled)
|
go udp.readLoop(cfg.Unhandled)
|
||||||
return udp.tab, udp, nil
|
return udp.tab, udp, nil
|
||||||
|
|
@ -274,7 +275,7 @@ func (t *udp) self() *enode.Node {
|
||||||
func (t *udp) close() {
|
func (t *udp) close() {
|
||||||
close(t.closing)
|
close(t.closing)
|
||||||
t.conn.Close()
|
t.conn.Close()
|
||||||
// TODO: wait for the loops to end.
|
t.wg.Wait()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *udp) ourEndpoint() rpcEndpoint {
|
func (t *udp) ourEndpoint() rpcEndpoint {
|
||||||
|
|
@ -379,6 +380,8 @@ func (t *udp) handleReply(from enode.ID, ptype byte, req packet) bool {
|
||||||
// loop runs in its own goroutine. it keeps track of
|
// loop runs in its own goroutine. it keeps track of
|
||||||
// the refresh timer and the pending reply queue.
|
// the refresh timer and the pending reply queue.
|
||||||
func (t *udp) loop() {
|
func (t *udp) loop() {
|
||||||
|
defer t.wg.Done()
|
||||||
|
|
||||||
var (
|
var (
|
||||||
plist = list.New()
|
plist = list.New()
|
||||||
timeout = time.NewTimer(0)
|
timeout = time.NewTimer(0)
|
||||||
|
|
@ -540,10 +543,11 @@ func encodePacket(priv *ecdsa.PrivateKey, ptype byte, req interface{}) (packet,
|
||||||
|
|
||||||
// readLoop runs in its own goroutine. it handles incoming UDP packets.
|
// readLoop runs in its own goroutine. it handles incoming UDP packets.
|
||||||
func (t *udp) readLoop(unhandled chan<- ReadPacket) {
|
func (t *udp) readLoop(unhandled chan<- ReadPacket) {
|
||||||
defer t.conn.Close()
|
defer t.wg.Done()
|
||||||
if unhandled != nil {
|
if unhandled != nil {
|
||||||
defer close(unhandled)
|
defer close(unhandled)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Discovery packets are defined to be no larger than 1280 bytes.
|
// Discovery packets are defined to be no larger than 1280 bytes.
|
||||||
// Packets larger than this size will be cut at the end and treated
|
// Packets larger than this size will be cut at the end and treated
|
||||||
// as invalid because their hash won't match.
|
// as invalid because their hash won't match.
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue