mirror of
https://github.com/rwinkhart/go-winio.git
synced 2026-09-06 00:57:19 -04:00
Merge pull request #70 from jstarks/connect_race
pipe: Resolve race between close and connect
This commit is contained in:
@@ -22,6 +22,7 @@ import (
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
cERROR_PIPE_BUSY = syscall.Errno(231)
|
cERROR_PIPE_BUSY = syscall.Errno(231)
|
||||||
|
cERROR_NO_DATA = syscall.Errno(232)
|
||||||
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
cERROR_PIPE_CONNECTED = syscall.Errno(535)
|
||||||
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
cERROR_SEM_TIMEOUT = syscall.Errno(121)
|
||||||
|
|
||||||
@@ -254,6 +255,36 @@ func (l *win32PipeListener) makeServerPipe() (*win32File, error) {
|
|||||||
return f, nil
|
return f, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (l *win32PipeListener) makeConnectedServerPipe() (*win32File, error) {
|
||||||
|
p, err := l.makeServerPipe()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the client to connect.
|
||||||
|
ch := make(chan error)
|
||||||
|
go func(p *win32File) {
|
||||||
|
ch <- connectPipe(p)
|
||||||
|
}(p)
|
||||||
|
|
||||||
|
select {
|
||||||
|
case err = <-ch:
|
||||||
|
if err != nil {
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
}
|
||||||
|
case <-l.closeCh:
|
||||||
|
// Abort the connect request by closing the handle.
|
||||||
|
p.Close()
|
||||||
|
p = nil
|
||||||
|
err = <-ch
|
||||||
|
if err == nil || err == ErrFileClosed {
|
||||||
|
err = ErrPipeListenerClosed
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return p, err
|
||||||
|
}
|
||||||
|
|
||||||
func (l *win32PipeListener) listenerRoutine() {
|
func (l *win32PipeListener) listenerRoutine() {
|
||||||
closed := false
|
closed := false
|
||||||
for !closed {
|
for !closed {
|
||||||
@@ -261,31 +292,20 @@ func (l *win32PipeListener) listenerRoutine() {
|
|||||||
case <-l.closeCh:
|
case <-l.closeCh:
|
||||||
closed = true
|
closed = true
|
||||||
case responseCh := <-l.acceptCh:
|
case responseCh := <-l.acceptCh:
|
||||||
p, err := l.makeServerPipe()
|
var (
|
||||||
if err == nil {
|
p *win32File
|
||||||
// Wait for the client to connect.
|
err error
|
||||||
ch := make(chan error)
|
)
|
||||||
go func(p *win32File) {
|
for {
|
||||||
ch <- connectPipe(p)
|
p, err = l.makeConnectedServerPipe()
|
||||||
}(p)
|
// If the connection was immediately closed by the client, try
|
||||||
select {
|
// again.
|
||||||
case err = <-ch:
|
if err != cERROR_NO_DATA {
|
||||||
if err != nil {
|
break
|
||||||
p.Close()
|
|
||||||
p = nil
|
|
||||||
}
|
|
||||||
case <-l.closeCh:
|
|
||||||
// Abort the connect request by closing the handle.
|
|
||||||
p.Close()
|
|
||||||
p = nil
|
|
||||||
err = <-ch
|
|
||||||
if err == nil || err == ErrFileClosed {
|
|
||||||
err = ErrPipeListenerClosed
|
|
||||||
}
|
|
||||||
closed = true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
responseCh <- acceptResponse{p, err}
|
responseCh <- acceptResponse{p, err}
|
||||||
|
closed = err == ErrPipeListenerClosed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
syscall.Close(l.firstHandle)
|
syscall.Close(l.firstHandle)
|
||||||
|
|||||||
@@ -422,3 +422,32 @@ func TestEchoWithMessaging(t *testing.T) {
|
|||||||
<-listenerDone
|
<-listenerDone
|
||||||
<-clientDone
|
<-clientDone
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestConnectRace(t *testing.T) {
|
||||||
|
l, err := ListenPipe(testPipeName, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
defer l.Close()
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
s, err := l.Accept()
|
||||||
|
if err == ErrPipeListenerClosed {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
s.Close()
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
for i := 0; i < 1000; i++ {
|
||||||
|
c, err := DialPipe(testPipeName, nil)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
c.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user