Skip to content

Commit 8ee3b79

Browse files
authored
fix: remove deadlock in multiPacketListener (#211)
* fix: remove deadlock in `multiPacketListener`. * fix test error * Move reading from the `PacketConn` outside the `select`. * Revert "fix test error" This reverts commit 552a1e6. * Do not set `m.pc` to nil. * Move buffer down. * Undo vpc creation. * Add mutex back to protect against concurrent `Close()` calls. * Move return when connection is closed down. * Do not close the `readCh` or exit on a closed PacketConn.
1 parent b561f49 commit 8ee3b79

File tree

1 file changed

+18
-17
lines changed

1 file changed

+18
-17
lines changed

service/listeners.go

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -128,42 +128,40 @@ type readRequest struct {
128128

129129
type virtualPacketConn struct {
130130
net.PacketConn
131-
mu sync.Mutex // Mutex to protect access to the channels
132-
readCh chan readRequest
131+
readCh chan readRequest
132+
133+
mu sync.Mutex // Mutex to protect against race conditions when closing the connection.
134+
closeCh chan struct{}
133135
onCloseFunc OnCloseFunc
134136
}
135137

136-
func (pc *virtualPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {
138+
func (pc *virtualPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {
137139
respCh := make(chan struct {
138140
n int
139141
addr net.Addr
140142
err error
141143
}, 1)
142144

143-
pc.mu.Lock()
144-
if pc.readCh == nil {
145-
pc.mu.Unlock()
146-
return 0, nil, net.ErrClosed
147-
}
148-
pc.readCh <- readRequest{
145+
select {
146+
case pc.readCh <- readRequest{
149147
buffer: p,
150148
respCh: respCh,
149+
}:
150+
case <-pc.closeCh:
151+
return 0, nil, net.ErrClosed
151152
}
152-
pc.mu.Unlock()
153153

154154
resp := <-respCh
155155
return resp.n, resp.addr, resp.err
156156
}
157157

158+
// Close closes the virtualPacketConn. It must be called once, and only once,
159+
// per virtualPacketConn.
158160
func (pc *virtualPacketConn) Close() error {
159161
pc.mu.Lock()
160162
defer pc.mu.Unlock()
161163

162-
if pc.readCh == nil {
163-
return nil
164-
}
165-
pc.readCh = nil
166-
164+
close(pc.closeCh)
167165
if pc.onCloseFunc != nil {
168166
onCloseFunc := pc.onCloseFunc
169167
pc.onCloseFunc = nil
@@ -287,10 +285,13 @@ func (m *multiPacketListener) Acquire() (net.PacketConn, error) {
287285
m.readCh = make(chan readRequest)
288286
m.doneCh = make(chan struct{})
289287
go func() {
288+
buffer := make([]byte, serverUDPBufferSize)
290289
for {
290+
n, addr, err := m.pc.ReadFrom(buffer)
291+
buffer = buffer[:n]
291292
select {
292293
case req := <-m.readCh:
293-
n, addr, err := pc.ReadFrom(req.buffer)
294+
n := copy(req.buffer, buffer)
294295
req.respCh <- struct {
295296
n int
296297
addr net.Addr
@@ -307,14 +308,14 @@ func (m *multiPacketListener) Acquire() (net.PacketConn, error) {
307308
return &virtualPacketConn{
308309
PacketConn: m.pc,
309310
readCh: m.readCh,
311+
closeCh: make(chan struct{}),
310312
onCloseFunc: func() error {
311313
m.mu.Lock()
312314
defer m.mu.Unlock()
313315
m.count--
314316
if m.count == 0 {
315317
close(m.doneCh)
316318
m.pc.Close()
317-
m.pc = nil
318319
if m.onCloseFunc != nil {
319320
onCloseFunc := m.onCloseFunc
320321
m.onCloseFunc = nil

0 commit comments

Comments
 (0)