watchtower/wtserver/server: fix race condition on Stop

This commit is contained in:
Conner Fromknecht 2019-03-15 02:33:33 -07:00
parent a222a63d81
commit e1e805d1b8
No known key found for this signature in database
GPG Key ID: E7D737B67FA592C7

@ -6,7 +6,6 @@ import (
"fmt" "fmt"
"net" "net"
"sync" "sync"
"sync/atomic"
"time" "time"
"github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/btcec"
@ -65,8 +64,8 @@ type Config struct {
// is to accept incoming connections, and dispatch processing of the client // is to accept incoming connections, and dispatch processing of the client
// message streams. // message streams.
type Server struct { type Server struct {
started int32 // atomic started sync.Once
shutdown int32 // atomic stopped sync.Once
cfg *Config cfg *Config
@ -75,6 +74,8 @@ type Server struct {
clientMtx sync.RWMutex clientMtx sync.RWMutex
clients map[wtdb.SessionID]Peer clients map[wtdb.SessionID]Peer
newPeers chan Peer
localInit *wtwire.Init localInit *wtwire.Init
wg sync.WaitGroup wg sync.WaitGroup
@ -93,6 +94,7 @@ func New(cfg *Config) (*Server, error) {
s := &Server{ s := &Server{
cfg: cfg, cfg: cfg,
clients: make(map[wtdb.SessionID]Peer), clients: make(map[wtdb.SessionID]Peer),
newPeers: make(chan Peer),
localInit: localInit, localInit: localInit,
quit: make(chan struct{}), quit: make(chan struct{}),
} }
@ -113,36 +115,31 @@ func New(cfg *Config) (*Server, error) {
// Start begins listening on the server's listeners. // Start begins listening on the server's listeners.
func (s *Server) Start() error { func (s *Server) Start() error {
// Already running? s.started.Do(func() {
if !atomic.CompareAndSwapInt32(&s.started, 0, 1) { log.Infof("Starting watchtower server")
return nil
}
log.Infof("Starting watchtower server") s.wg.Add(1)
go s.peerHandler()
s.connMgr.Start() s.connMgr.Start()
log.Infof("Watchtower server started successfully")
log.Infof("Watchtower server started successfully")
})
return nil return nil
} }
// Stop shutdowns down the server's listeners and any active requests. // Stop shutdowns down the server's listeners and any active requests.
func (s *Server) Stop() error { func (s *Server) Stop() error {
// Bail if we're already shutting down. s.stopped.Do(func() {
if !atomic.CompareAndSwapInt32(&s.shutdown, 0, 1) { log.Infof("Stopping watchtower server")
return nil
}
log.Infof("Stopping watchtower server") s.connMgr.Stop()
s.connMgr.Stop() close(s.quit)
s.wg.Wait()
close(s.quit)
s.wg.Wait()
log.Infof("Watchtower server stopped successfully")
log.Infof("Watchtower server stopped successfully")
})
return nil return nil
} }
@ -167,8 +164,29 @@ func (s *Server) inboundPeerConnected(c net.Conn) {
// by the client. This method serves also as a public endpoint for locally // by the client. This method serves also as a public endpoint for locally
// registering new clients with the server. // registering new clients with the server.
func (s *Server) InboundPeerConnected(peer Peer) { func (s *Server) InboundPeerConnected(peer Peer) {
s.wg.Add(1) select {
go s.handleClient(peer) case s.newPeers <- peer:
case <-s.quit:
}
}
// peerHandler processes newly accepted peers and spawns a client handler for
// each. The peerHandler is used to ensure that waitgrouped client handlers are
// spawned from a waitgrouped goroutine.
func (s *Server) peerHandler() {
defer s.wg.Done()
defer s.removeAllPeers()
for {
select {
case peer := <-s.newPeers:
s.wg.Add(1)
go s.handleClient(peer)
case <-s.quit:
return
}
}
} }
// handleClient processes a series watchtower messages sent by a client. The // handleClient processes a series watchtower messages sent by a client. The
@ -625,6 +643,21 @@ func (s *Server) removePeer(id *wtdb.SessionID, addr net.Addr) {
} }
} }
// removeAllPeers iterates through the server's current set of peers and closes
// all open connections.
func (s *Server) removeAllPeers() {
s.clientMtx.Lock()
defer s.clientMtx.Unlock()
for id, peer := range s.clients {
log.Infof("Releasing incoming peer %s@%s", id,
peer.RemoteAddr())
delete(s.clients, id)
peer.Close()
}
}
// logMessage writes information about a message exchanged with a remote peer, // logMessage writes information about a message exchanged with a remote peer,
// using directional prepositions to signal whether the message was sent or // using directional prepositions to signal whether the message was sent or
// received. // received.