25
"time"
26
)
27
28
-// TODO: Can we use the new UsagePool type?
29
-
28
// Listen returns a listener suitable for use in a Caddy module.
29
// Always be sure to close listeners when you are done with them.
30
func Listen(network, addr string) (net.Listener, error) {
34
defer listenersMu.Unlock()
35
36
// if listener already exists, increment usage counter, then return listener
39
- if lnUsage, ok := listeners[lnKey]; ok {
40
- atomic.AddInt32(&lnUsage.usage, 1)
41
- return &fakeCloseListener{usage: &lnUsage.usage, key: lnKey, Listener: lnUsage.ln}, nil
37
+ if lnGlobal, ok := listeners[lnKey]; ok {
38
+ atomic.AddInt32(&lnGlobal.usage, 1)
39
+ return &fakeCloseListener{
40
+ usage: &lnGlobal.usage,
41
+ deadline: &lnGlobal.deadline,
42
+ deadlineMu: &lnGlobal.deadlineMu,
43
+ key: lnKey,
44
+ Listener: lnGlobal.ln,
45
+ }, nil
46
}
47
48
// or, create new one and save it
52
}
53
54
// make sure to start its usage counter at 1
51
- lnUsage := &listenerUsage{usage: 1, ln: ln}
52
- listeners[lnKey] = lnUsage
53
-
54
- return &fakeCloseListener{usage: &lnUsage.usage, key: lnKey, Listener: ln}, nil
55
+ lnGlobal := &globalListener{
56
+ usage: 1,
57
+ ln: ln,
58
+ }
59
+ listeners[lnKey] = lnGlobal
60
+
61
+ return &fakeCloseListener{
62
+ usage: &lnGlobal.usage,
63
+ deadline: &lnGlobal.deadline,
64
+ deadlineMu: &lnGlobal.deadlineMu,
65
+ key: lnKey,
66
+ Listener: ln,
67
+ }, nil
68
}
69
70
// ListenPacket returns a net.PacketConn suitable for use in a Caddy module.
76
defer listenersMu.Unlock()
77
78
// if listener already exists, increment usage counter, then return listener
66
- if lnUsage, ok := listeners[lnKey]; ok {
67
- atomic.AddInt32(&lnUsage.usage, 1)
68
- log.Printf("[DEBUG] %s: Usage counter should not go above 2 or maybe 3, is now: %d", lnKey, atomic.LoadInt32(&lnUsage.usage)) // TODO: remove
69
- return &fakeClosePacketConn{usage: &lnUsage.usage, key: lnKey, PacketConn: lnUsage.pc}, nil
79
+ if lnGlobal, ok := listeners[lnKey]; ok {
80
+ atomic.AddInt32(&lnGlobal.usage, 1)
81
+ log.Printf("[DEBUG] %s: Usage counter should not go above 2 or maybe 3, is now: %d", lnKey, atomic.LoadInt32(&lnGlobal.usage)) // TODO: remove
82
+ return &fakeClosePacketConn{usage: &lnGlobal.usage, key: lnKey, PacketConn: lnGlobal.pc}, nil
83
}
84
85
// or, create new one and save it
89
}
90
91
// make sure to start its usage counter at 1
79
- lnUsage := &listenerUsage{usage: 1, pc: pc}
80
- listeners[lnKey] = lnUsage
92
+ lnGlobal := &globalListener{usage: 1, pc: pc}
93
+ listeners[lnKey] = lnGlobal
94
82
- return &fakeClosePacketConn{usage: &lnUsage.usage, key: lnKey, PacketConn: pc}, nil
95
+ return &fakeClosePacketConn{usage: &lnGlobal.usage, key: lnKey, PacketConn: pc}, nil
96
}
97
98
// fakeCloseListener's Close() method is a no-op. This allows
100
// up the socket; thus, servers become hot-swappable while the
101
// listener remains running. Listeners should be re-wrapped in
102
// a new fakeCloseListener each time the listener is reused.
103
+// Other than the 'closed' field (which pertains to this value
104
+// only), the other fields in this struct should be pointers to
105
+// the associated globalListener's struct fields (except 'key'
106
+// which is there for read-only purposes, so it can be a copy).
107
type fakeCloseListener struct {
91
- closed int32 // accessed atomically - TODO: this needs to be shared across the whole app instance, not to cross instance boundaries... hmmm... see #2658 (still relevant?)
92
- usage *int32 // accessed atomically
93
- key string
94
- net.Listener
108
+ closed int32 // accessed atomically; belongs to this struct only
109
+ usage *int32 // accessed atomically; global
110
+ deadline *bool // protected by deadlineMu; global
111
+ deadlineMu *sync.Mutex // global
112
+ key string // global, but read-only, so can be copy
113
+ net.Listener // global
114
}
115
116
// Accept accepts connections until Close() is called.
126
return conn, nil
127
}
128
110
- if atomic.LoadInt32(&fcl.closed) == 1 {
111
- // clear the deadline
129
+ // accept returned with error
130
+ // TODO: This may be better as a condition variable so the deadline is cleared only once?
131
+ fcl.deadlineMu.Lock()
132
+ if *fcl.deadline {
133
switch ln := fcl.Listener.(type) {
134
case *net.TCPListener:
135
ln.SetDeadline(time.Time{})
136
case *net.UnixListener:
137
ln.SetDeadline(time.Time{})
138
}
139
+ *fcl.deadline = false
140
+ }
141
+ fcl.deadlineMu.Unlock()
142
143
+ if atomic.LoadInt32(&fcl.closed) == 1 {
144
// if we cancelled the Accept() by setting a deadline
145
// on the listener, we need to make sure any callers of
146
// Accept() think the listener was actually closed;
166
// a deadline in the past, which forces it to
167
// time out; note that this only works for
168
// certain types of listeners...
144
- switch ln := fcl.Listener.(type) {
145
- case *net.TCPListener:
146
- ln.SetDeadline(time.Now().Add(-1 * time.Minute))
147
- case *net.UnixListener:
148
- ln.SetDeadline(time.Now().Add(-1 * time.Minute))
169
+ fcl.deadlineMu.Lock()
170
+ if !*fcl.deadline {
171
+ switch ln := fcl.Listener.(type) {
172
+ case *net.TCPListener:
173
+ ln.SetDeadline(time.Now().Add(-1 * time.Minute))
174
+ case *net.UnixListener:
175
+ ln.SetDeadline(time.Now().Add(-1 * time.Minute))
176
+ }
177
+ *fcl.deadline = true
178
}
179
+ fcl.deadlineMu.Unlock()
180
181
// since we're no longer using this listener,
182
// decrement the usage counter and, if no one
206
}
207
208
type fakeClosePacketConn struct {
179
- closed int32 // accessed atomically - TODO: this needs to be shared across the whole app instance, not to cross instance boundaries... hmmm... see #2658 (still relevant?)
209
+ closed int32 // accessed atomically
210
usage *int32 // accessed atomically
211
key string
212
net.PacketConn
240
// socket is actually left open.
241
var errFakeClosed = fmt.Errorf("listener 'closed' 😉")
242
213
-// listenerUsage pairs a net.Listener with a
214
-// count of how many servers are using it.
215
-type listenerUsage struct {
216
- usage int32 // accessed atomically
217
- ln net.Listener
218
- pc net.PacketConn
243
+// globalListener keeps global state for a listener
244
+// that may be shared by multiple servers. In other
245
+// words, values in this struct exist only once and
246
+// all other uses of these values point to the ones
247
+// in this struct. In particular, the usage count
248
+// (how many callers are using the listener), the
249
+// actual listener, and synchronization of the
250
+// listener's deadline changes are singular, global
251
+// values that must not be copied.
252
+type globalListener struct {
253
+ usage int32 // accessed atomically
254
+ deadline bool
255
+ deadlineMu sync.Mutex
256
+ ln net.Listener
257
+ pc net.PacketConn
258
}
259
260
var (
222
- listeners = make(map[string]*listenerUsage)
261
+ listeners = make(map[string]*globalListener)
262
listenersMu sync.Mutex
263
)
264