Skip to content

Commit b7c3cf0

Browse files
author
John Howard
authored
Merge pull request #70 from jstarks/connect_race
pipe: Resolve race between close and connect
2 parents 7843996 + ae842e4 commit b7c3cf0

File tree

2 files changed

+71
-22
lines changed

2 files changed

+71
-22
lines changed

pipe.go

Lines changed: 42 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import (
2222

2323
const (
2424
cERROR_PIPE_BUSY = syscall.Errno(231)
25+
cERROR_NO_DATA = syscall.Errno(232)
2526
cERROR_PIPE_CONNECTED = syscall.Errno(535)
2627
cERROR_SEM_TIMEOUT = syscall.Errno(121)
2728

@@ -254,38 +255,57 @@ func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
254255
return f, nil
255256
}
256257

258+
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
259+
p, err := l.makeServerPipe()
260+
if err != nil {
261+
return nil, err
262+
}
263+
264+
// Wait for the client to connect.
265+
ch := make(chan error)
266+
go func(p *win32File) {
267+
ch <- connectPipe(p)
268+
}(p)
269+
270+
select {
271+
case err = <-ch:
272+
if err != nil {
273+
p.Close()
274+
p = nil
275+
}
276+
case <-l.closeCh:
277+
// Abort the connect request by closing the handle.
278+
p.Close()
279+
p = nil
280+
err = <-ch
281+
if err == nil || err == ErrFileClosed {
282+
err = ErrPipeListenerClosed
283+
}
284+
}
285+
return p, err
286+
}
287+
257288
func (l *win32PipeListener) listenerRoutine() {
258289
closed := false
259290
for !closed {
260291
select {
261292
case <-l.closeCh:
262293
closed = true
263294
case responseCh := <-l.acceptCh:
264-
p, err := l.makeServerPipe()
265-
if err == nil {
266-
// Wait for the client to connect.
267-
ch := make(chan error)
268-
go func(p *win32File) {
269-
ch <- connectPipe(p)
270-
}(p)
271-
select {
272-
case err = <-ch:
273-
if err != nil {
274-
p.Close()
275-
p = nil
276-
}
277-
case <-l.closeCh:
278-
// Abort the connect request by closing the handle.
279-
p.Close()
280-
p = nil
281-
err = <-ch
282-
if err == nil || err == ErrFileClosed {
283-
err = ErrPipeListenerClosed
284-
}
285-
closed = true
295+
var (
296+
p *win32File
297+
err error
298+
)
299+
for {
300+
p, err = l.makeConnectedServerPipe()
301+
// If the connection was immediately closed by the client, try
302+
// again.
303+
if err != cERROR_NO_DATA {
304+
break
286305
}
287306
}
288307
responseCh <- acceptResponse{p, err}
308+
closed = err == ErrPipeListenerClosed
289309
}
290310
}
291311
syscall.Close(l.firstHandle)

pipe_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -422,3 +422,32 @@ func TestEchoWithMessaging(t *testing.T) {
422422
<-listenerDone
423423
<-clientDone
424424
}
425+
426+
func TestConnectRace(t *testing.T) {
427+
l, err := ListenPipe(testPipeName, nil)
428+
if err != nil {
429+
t.Fatal(err)
430+
}
431+
defer l.Close()
432+
go func() {
433+
for {
434+
s, err := l.Accept()
435+
if err == ErrPipeListenerClosed {
436+
return
437+
}
438+
439+
if err != nil {
440+
t.Fatal(err)
441+
}
442+
s.Close()
443+
}
444+
}()
445+
446+
for i := 0; i < 1000; i++ {
447+
c, err := DialPipe(testPipeName, nil)
448+
if err != nil {
449+
t.Fatal(err)
450+
}
451+
c.Close()
452+
}
453+
}

0 commit comments

Comments
 (0)