Skip to content

Commit

Permalink
[release-branch.go1.9] internal/poll: do not call SetFileCompletionNo…
Browse files Browse the repository at this point in the history
…tificationModes if it is broken

Current code assumes that SetFileCompletionNotificationModes
is safe to call even if we know that it is not safe to use
FILE_SKIP_COMPLETION_PORT_ON_SUCCESS flag. It appears (see issue #22149),
SetFileCompletionNotificationModes crashes when we call it without
FILE_SKIP_COMPLETION_PORT_ON_SUCCESS flag.

Do not call SetFileCompletionNotificationModes in that situation.
We are allowed to do that, because SetFileCompletionNotificationModes
is just an optimisation.

Fixes #22149

Change-Id: I0ad3aff4eabd8c27739417a62c286b1819ae166a
Reviewed-on: https://go-review.googlesource.com/69870
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Reviewed-on: https://go-review.googlesource.com/70989
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Alex Brainman <alex.brainman@gmail.com>
  • Loading branch information
alexbrainman authored and rsc committed Oct 25, 2017
1 parent 39d4bb9 commit f259aed
Showing 1 changed file with 37 additions and 27 deletions.
64 changes: 37 additions & 27 deletions src/internal/poll/fd_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,40 @@ var (
// package uses CancelIoEx API, if present, otherwise it fallback
// to CancelIo.

var (
canCancelIO bool // determines if CancelIoEx API is present
skipSyncNotif bool
hasLoadSetFileCompletionNotificationModes bool
)
var canCancelIO bool // determines if CancelIoEx API is present

// This package uses SetFileCompletionNotificationModes Windows API
// to skip calling GetQueuedCompletionStatus if an IO operation completes
// synchronously. Unfortuently SetFileCompletionNotificationModes is not
// available on Windows XP. Also there is a known bug where
// SetFileCompletionNotificationModes crashes on some systems
// (see http://support.microsoft.com/kb/2568167 for details).

var useSetFileCompletionNotificationModes bool // determines is SetFileCompletionNotificationModes is present and safe to use

// checkSetFileCompletionNotificationModes verifies that
// SetFileCompletionNotificationModes Windows API is present
// on the system and is safe to use.
// See http://support.microsoft.com/kb/2568167 for details.
func checkSetFileCompletionNotificationModes() {
err := syscall.LoadSetFileCompletionNotificationModes()
if err != nil {
return
}
protos := [2]int32{syscall.IPPROTO_TCP, 0}
var buf [32]syscall.WSAProtocolInfo
len := uint32(unsafe.Sizeof(buf))
n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len)
if err != nil {
return
}
for i := int32(0); i < n; i++ {
if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 {
return
}
}
useSetFileCompletionNotificationModes = true
}

func init() {
var d syscall.WSAData
Expand All @@ -44,26 +73,7 @@ func init() {
initErr = e
}
canCancelIO = syscall.LoadCancelIoEx() == nil
hasLoadSetFileCompletionNotificationModes = syscall.LoadSetFileCompletionNotificationModes() == nil
if hasLoadSetFileCompletionNotificationModes {
// It's not safe to use FILE_SKIP_COMPLETION_PORT_ON_SUCCESS if non IFS providers are installed:
// http://support.microsoft.com/kb/2568167
skipSyncNotif = true
protos := [2]int32{syscall.IPPROTO_TCP, 0}
var buf [32]syscall.WSAProtocolInfo
len := uint32(unsafe.Sizeof(buf))
n, err := syscall.WSAEnumProtocols(&protos[0], &buf[0], &len)
if err != nil {
skipSyncNotif = false
} else {
for i := int32(0); i < n; i++ {
if buf[i].ServiceFlags1&syscall.XP1_IFS_HANDLES == 0 {
skipSyncNotif = false
break
}
}
}
}
checkSetFileCompletionNotificationModes()
}

// operation contains superset of data necessary to perform all async IO.
Expand Down Expand Up @@ -344,12 +354,12 @@ func (fd *FD) Init(net string, pollable bool) (string, error) {
if err != nil {
return "", err
}
if hasLoadSetFileCompletionNotificationModes {
if useSetFileCompletionNotificationModes {
// We do not use events, so we can skip them always.
flags := uint8(syscall.FILE_SKIP_SET_EVENT_ON_HANDLE)
// It's not safe to skip completion notifications for UDP:
// http://blogs.technet.com/b/winserverperformance/archive/2008/06/26/designing-applications-for-high-performance-part-iii.aspx
if skipSyncNotif && (net == "tcp" || net == "file") {
if net == "tcp" || net == "file" {
flags |= syscall.FILE_SKIP_COMPLETION_PORT_ON_SUCCESS
}
err := syscall.SetFileCompletionNotificationModes(fd.Sysfd, flags)
Expand Down

0 comments on commit f259aed

Please sign in to comment.