@marcel91200 / caddy Mirror · read-only / commits / 59a5d0db
Fork 0

Fast and extensible multi-platform HTTP/1-2-3 web server with automatic HTTPS

Mirrored from https://github.com/Slazee/caddy.git

Synced Nov 14, 05:15 UTC

Close listeners which are no longer used

Matthew Holt committed Apr 2, 2019 at 15:31 UTC 59a5d0db28e36b9732bc9ad2b73bcde9188e207c
2 files changed +40 -12
caddy.go
+17
@@ -6,6 +6,7 @@ import (
6 "log"
7 "strings"
8 "sync"
9 + "sync/atomic"
10 "time"
11 )
12
@@ -24,6 +25,7 @@ func Start(cfg Config) error {
25 cfg.runners[modName] = val.(Runner)
26 }
27
28 + // start the new runners
29 for name, r := range cfg.runners {
30 err := r.Run()
31 if err != nil {
@@ -32,6 +34,7 @@ func Start(cfg Config) error {
34 }
35 }
36
37 + // shut down down the old ones
38 currentCfgMu.Lock()
39 if currentCfg != nil {
40 for _, r := range currentCfg.runners {
@@ -44,6 +47,20 @@ func Start(cfg Config) error {
47 currentCfg = &cfg
48 currentCfgMu.Unlock()
49
50 + // shut down listeners that are no longer being used
51 + listenersMu.Lock()
52 + for key, info := range listeners {
53 + if atomic.LoadInt32(&info.usage) == 0 {
54 + err := info.ln.Close()
55 + if err != nil {
56 + log.Printf("[ERROR] closing listener %s: %v", info.ln.Addr(), err)
57 + continue
58 + }
59 + delete(listeners, key)
60 + }
61 + }
62 + listenersMu.Unlock()
63 +
64 return nil
65 }
66
listeners.go
+23 -12
@@ -15,9 +15,10 @@ func Listen(network, addr string) (net.Listener, error) {
15 listenersMu.Lock()
16 defer listenersMu.Unlock()
17
18 - // if listener already exists, return it
19 - if ln, ok := listeners[lnKey]; ok {
20 - return &fakeCloseListener{Listener: ln}, nil
18 + // if listener already exists, increment usage counter, then return listener
19 + if lnInfo, ok := listeners[lnKey]; ok {
20 + atomic.AddInt32(&lnInfo.usage, 1)
21 + return &fakeCloseListener{usage: &lnInfo.usage, Listener: lnInfo.ln}, nil
22 }
23
24 // or, create new one and save it
@@ -25,9 +26,12 @@ func Listen(network, addr string) (net.Listener, error) {
26 if err != nil {
27 return nil, err
28 }
28 - listeners[lnKey] = ln
29
30 - return &fakeCloseListener{Listener: ln}, nil
30 + // make sure to start its usage counter at 1
31 + lnInfo := &listenerUsage{usage: 1, ln: ln}
32 + listeners[lnKey] = lnInfo
33 +
34 + return &fakeCloseListener{usage: &lnInfo.usage, Listener: ln}, nil
35 }
36
37 // fakeCloseListener's Close() method is a no-op. This allows
@@ -36,7 +40,8 @@ func Listen(network, addr string) (net.Listener, error) {
40 // listener remains running. Listeners should be re-wrapped in
41 // a new fakeCloseListener each time the listener is reused.
42 type fakeCloseListener struct {
39 - closed int32
43 + closed int32 // accessed atomically
44 + usage *int32 // accessed atomically
45 net.Listener
46 }
47
@@ -92,16 +97,15 @@ func (fcl *fakeCloseListener) Close() error {
97 case *net.UnixListener:
98 ln.SetDeadline(time.Now().Add(-1 * time.Minute))
99 }
100 +
101 + // since we're no longer using this listener,
102 + // decrement the usage counter
103 + atomic.AddInt32(fcl.usage, -1)
104 }
105
106 return nil
107 }
108
100 -// CloseUnderlying actually closes the underlying listener.
101 -func (fcl *fakeCloseListener) CloseUnderlying() error {
102 - return fcl.Listener.Close()
103 -}
104 -
109 func (fcl *fakeCloseListener) fakeClosedErr() error {
110 return &net.OpError{
111 Op: "accept",
@@ -118,7 +122,14 @@ func (fcl *fakeCloseListener) fakeClosedErr() error {
122 // socket is actually left open.
123 var ErrFakeClosed = fmt.Errorf("listener 'closed' 😉")
124
125 +// listenerUsage pairs a net.Listener with a
126 +// count of how many servers are using it.
127 +type listenerUsage struct {
128 + usage int32 // accessed atomically
129 + ln net.Listener
130 +}
131 +
132 var (
122 - listeners = make(map[string]net.Listener)
133 + listeners = make(map[string]*listenerUsage)
134 listenersMu sync.Mutex
135 )