Skip to content

Commit a6d595a

Browse files
authored
Merge pull request #80 from nanomsg/norace2
fixes #67 DialPipe problem with multiple calls / waiting for busy pipe
2 parents 6792112 + effecfb commit a6d595a

File tree

1 file changed

+22
-20
lines changed

1 file changed

+22
-20
lines changed

pipe.go

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import (
1515
//sys connectNamedPipe(pipe syscall.Handle, o *syscall.Overlapped) (err error) = ConnectNamedPipe
1616
//sys createNamedPipe(name string, flags uint32, pipeMode uint32, maxInstances uint32, outSize uint32, inSize uint32, defaultTimeout uint32, sa *syscall.SecurityAttributes) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateNamedPipeW
1717
//sys createFile(name string, access uint32, mode uint32, sa *syscall.SecurityAttributes, createmode uint32, attrs uint32, templatefile syscall.Handle) (handle syscall.Handle, err error) [failretval==syscall.InvalidHandle] = CreateFileW
18-
//sys waitNamedPipe(name string, timeout uint32) (err error) = WaitNamedPipeW
1918
//sys getNamedPipeInfo(pipe syscall.Handle, flags *uint32, outSize *uint32, inSize *uint32, maxInstances *uint32) (err error) = GetNamedPipeInfo
2019
//sys getNamedPipeHandleState(pipe syscall.Handle, state *uint32, curInstances *uint32, maxCollectionCount *uint32, collectDataTimeout *uint32, userName *uint16, maxUserNameSize uint32) (err error) = GetNamedPipeHandleStateW
2120
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc
@@ -139,12 +138,14 @@ func (s pipeAddress) String() string {
139138
}
140139

141140
// DialPipe connects to a named pipe by path, timing out if the connection
142-
// takes longer than the specified duration. If timeout is nil, then the timeout
143-
// is the default timeout established by the pipe server.
141+
// takes longer than the specified duration. If timeout is nil, then we use
142+
// a default timeout of 5 seconds. (We do not use WaitNamedPipe.)
144143
func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
145144
var absTimeout time.Time
146145
if timeout != nil {
147146
absTimeout = time.Now().Add(*timeout)
147+
} else {
148+
absTimeout = time.Now().Add(time.Second * 2)
148149
}
149150
var err error
150151
var h syscall.Handle
@@ -153,22 +154,13 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) {
153154
if err != cERROR_PIPE_BUSY {
154155
break
155156
}
156-
now := time.Now()
157-
var ms uint32
158-
if absTimeout.IsZero() {
159-
ms = cNMPWAIT_USE_DEFAULT_WAIT
160-
} else if now.After(absTimeout) {
161-
ms = cNMPWAIT_NOWAIT
162-
} else {
163-
ms = uint32(absTimeout.Sub(now).Nanoseconds() / 1000 / 1000)
164-
}
165-
err = waitNamedPipe(path, ms)
166-
if err != nil {
167-
if err == cERROR_SEM_TIMEOUT {
168-
return nil, ErrTimeout
169-
}
170-
break
157+
if time.Now().After(absTimeout) {
158+
return nil, ErrTimeout
171159
}
160+
161+
// Wait 10 msec and try again. This is a rather simplistic
162+
// view, as we always try each 10 milliseconds.
163+
time.Sleep(time.Millisecond * 10)
172164
}
173165
if err != nil {
174166
return nil, &os.PathError{Op: "open", Path: path, Err: err}
@@ -349,13 +341,23 @@ func ListenPipe(path string, c *PipeConfig) (net.Listener, error) {
349341
if err != nil {
350342
return nil, err
351343
}
352-
// Immediately open and then close a client handle so that the named pipe is
353-
// created but not currently accepting connections.
344+
// Create a client handle and connect it. This results in the pipe
345+
// instance always existing, so that clients see ERROR_PIPE_BUSY
346+
// rather than ERROR_FILE_NOT_FOUND. This ties the first instance
347+
// up so that no other instances can be used. This would have been
348+
// cleaner if the Win32 API matched CreateFile with ConnectNamedPipe
349+
// instead of CreateNamedPipe. (Apparently created named pipes are
350+
// considered to be in listening state regardless of whether any
351+
// active calls to ConnectNamedPipe are outstanding.)
354352
h2, err := createFile(path, 0, 0, nil, syscall.OPEN_EXISTING, cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0)
355353
if err != nil {
356354
syscall.Close(h)
357355
return nil, err
358356
}
357+
// Close the client handle. The server side of the instance will
358+
// still be busy, leading to ERROR_PIPE_BUSY instead of
359+
// ERROR_NOT_FOUND, as long as we don't close the server handle,
360+
// or disconnect the client with DisconnectNamedPipe.
359361
syscall.Close(h2)
360362
l := &win32PipeListener{
361363
firstHandle: h,

0 commit comments

Comments
 (0)