Close listeners which are no longer used

This commit is contained in:
Matthew Holt 2019-04-02 15:31:02 -06:00
parent f976aa7443
commit 59a5d0db28
2 changed files with 40 additions and 12 deletions

View file

@ -6,6 +6,7 @@ import (
"log" "log"
"strings" "strings"
"sync" "sync"
"sync/atomic"
"time" "time"
) )
@ -24,6 +25,7 @@ func Start(cfg Config) error {
cfg.runners[modName] = val.(Runner) cfg.runners[modName] = val.(Runner)
} }
// start the new runners
for name, r := range cfg.runners { for name, r := range cfg.runners {
err := r.Run() err := r.Run()
if err != nil { if err != nil {
@ -32,6 +34,7 @@ func Start(cfg Config) error {
} }
} }
// shut down down the old ones
currentCfgMu.Lock() currentCfgMu.Lock()
if currentCfg != nil { if currentCfg != nil {
for _, r := range currentCfg.runners { for _, r := range currentCfg.runners {
@ -44,6 +47,20 @@ func Start(cfg Config) error {
currentCfg = &cfg currentCfg = &cfg
currentCfgMu.Unlock() currentCfgMu.Unlock()
// shut down listeners that are no longer being used
listenersMu.Lock()
for key, info := range listeners {
if atomic.LoadInt32(&info.usage) == 0 {
err := info.ln.Close()
if err != nil {
log.Printf("[ERROR] closing listener %s: %v", info.ln.Addr(), err)
continue
}
delete(listeners, key)
}
}
listenersMu.Unlock()
return nil return nil
} }

View file

@ -15,9 +15,10 @@ func Listen(network, addr string) (net.Listener, error) {
listenersMu.Lock() listenersMu.Lock()
defer listenersMu.Unlock() defer listenersMu.Unlock()
// if listener already exists, return it // if listener already exists, increment usage counter, then return listener
if ln, ok := listeners[lnKey]; ok { if lnInfo, ok := listeners[lnKey]; ok {
return &fakeCloseListener{Listener: ln}, nil atomic.AddInt32(&lnInfo.usage, 1)
return &fakeCloseListener{usage: &lnInfo.usage, Listener: lnInfo.ln}, nil
} }
// or, create new one and save it // or, create new one and save it
@ -25,9 +26,12 @@ func Listen(network, addr string) (net.Listener, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
listeners[lnKey] = ln
return &fakeCloseListener{Listener: ln}, nil // make sure to start its usage counter at 1
lnInfo := &listenerUsage{usage: 1, ln: ln}
listeners[lnKey] = lnInfo
return &fakeCloseListener{usage: &lnInfo.usage, Listener: ln}, nil
} }
// fakeCloseListener's Close() method is a no-op. This allows // fakeCloseListener's Close() method is a no-op. This allows
@ -36,7 +40,8 @@ func Listen(network, addr string) (net.Listener, error) {
// listener remains running. Listeners should be re-wrapped in // listener remains running. Listeners should be re-wrapped in
// a new fakeCloseListener each time the listener is reused. // a new fakeCloseListener each time the listener is reused.
type fakeCloseListener struct { type fakeCloseListener struct {
closed int32 closed int32 // accessed atomically
usage *int32 // accessed atomically
net.Listener net.Listener
} }
@ -92,16 +97,15 @@ func (fcl *fakeCloseListener) Close() error {
case *net.UnixListener: case *net.UnixListener:
ln.SetDeadline(time.Now().Add(-1 * time.Minute)) ln.SetDeadline(time.Now().Add(-1 * time.Minute))
} }
// since we're no longer using this listener,
// decrement the usage counter
atomic.AddInt32(fcl.usage, -1)
} }
return nil return nil
} }
// CloseUnderlying actually closes the underlying listener.
func (fcl *fakeCloseListener) CloseUnderlying() error {
return fcl.Listener.Close()
}
func (fcl *fakeCloseListener) fakeClosedErr() error { func (fcl *fakeCloseListener) fakeClosedErr() error {
return &net.OpError{ return &net.OpError{
Op: "accept", Op: "accept",
@ -118,7 +122,14 @@ func (fcl *fakeCloseListener) fakeClosedErr() error {
// socket is actually left open. // socket is actually left open.
var ErrFakeClosed = fmt.Errorf("listener 'closed' 😉") var ErrFakeClosed = fmt.Errorf("listener 'closed' 😉")
// listenerUsage pairs a net.Listener with a
// count of how many servers are using it.
type listenerUsage struct {
usage int32 // accessed atomically
ln net.Listener
}
var ( var (
listeners = make(map[string]net.Listener) listeners = make(map[string]*listenerUsage)
listenersMu sync.Mutex listenersMu sync.Mutex
) )