-
Notifications
You must be signed in to change notification settings - Fork 185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Retry named pipe connection on race condition #75
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import ( | |
"io" | ||
"net" | ||
"os" | ||
"math/rand" | ||
"syscall" | ||
"time" | ||
"unsafe" | ||
|
@@ -21,6 +22,8 @@ import ( | |
//sys localAlloc(uFlags uint32, length uint32) (ptr uintptr) = LocalAlloc | ||
|
||
const ( | ||
cERROR_FILE_NOT_FOUND = 2 | ||
cERROR_BAD_PATHNAME = 161 | ||
cERROR_PIPE_BUSY = syscall.Errno(231) | ||
cERROR_NO_DATA = syscall.Errno(232) | ||
cERROR_PIPE_CONNECTED = syscall.Errno(535) | ||
|
@@ -62,6 +65,21 @@ type win32MessageBytePipe struct { | |
readEOF bool | ||
} | ||
|
||
|
||
func shouldRetry(err error) bool { | ||
if errno, ok := err.(syscall.Errno); ok { | ||
errorCode := int(errno) | ||
if errorCode == cERROR_FILE_NOT_FOUND { | ||
return true | ||
} | ||
if errorCode == cERROR_BAD_PATHNAME { | ||
return true | ||
} | ||
} | ||
return false | ||
} | ||
|
||
|
||
type pipeAddress string | ||
|
||
func (f *win32Pipe) LocalAddr() net.Addr { | ||
|
@@ -145,6 +163,13 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { | |
var h syscall.Handle | ||
for { | ||
h, err = createFile(path, syscall.GENERIC_READ|syscall.GENERIC_WRITE, 0, nil, syscall.OPEN_EXISTING, syscall.FILE_FLAG_OVERLAPPED|cSECURITY_SQOS_PRESENT|cSECURITY_ANONYMOUS, 0) | ||
if err == nil { | ||
break | ||
} | ||
if shouldRetry(err) { | ||
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) | ||
continue | ||
} | ||
if err != cERROR_PIPE_BUSY { | ||
break | ||
} | ||
|
@@ -159,6 +184,10 @@ func DialPipe(path string, timeout *time.Duration) (net.Conn, error) { | |
} | ||
err = waitNamedPipe(path, ms) | ||
if err != nil { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think I'd have restructured this so that you always loop around to CreateFile. What you do need to do is bail if the time is after the absolute timeout above. (Like 156/181). In fact, If the time out above has expired, you should simply bail, not go down into waitNamedPipe again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Put another way, all waitNamedPipe() is doing for you is putting you to sleep a bit so you don't spin hard. The consequences of its error returns are simply not very interesting. In some other project I didn't even use this call at all, and just did a 10 ms sleep between retries... |
||
if shouldRetry(err) { | ||
time.Sleep(time.Duration(rand.Intn(10)) * time.Millisecond) | ||
continue | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If we continue here, do we have to close There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Unless I misunderstood, for {
h, err = createFile(path, ...)
if err == nil {
break
}
...
err = waitNamedPipe(path, ...)
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Correct. Now I see it (on a bigger screen than my mobile phone, haha :-) ). |
||
} | ||
if err == cERROR_SEM_TIMEOUT { | ||
return nil, ErrTimeout | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This looks to me like it creates an infinite loop if there really is no pipe server present. That's bad.
There needs to be something to catch this. Probably what you could do is move this check to after the timekeeping validation.