aboutsummaryrefslogtreecommitdiff
path: root/src/internal
diff options
context:
space:
mode:
authorqmuntal <quimmuntal@gmail.com>2025-04-10 16:03:46 +0200
committerGopher Robot <gobot@golang.org>2025-04-14 05:40:53 -0700
commitf414dfe4f5049c2c8998b4e6b90dee7fca0c225b (patch)
tree53107a6dc07e3d1321d2bc65e74a5f43c801f74d /src/internal
parent13b7c7d8d21765886697c952ffbb7fb853a2bf9a (diff)
downloadgo-f414dfe4f5049c2c8998b4e6b90dee7fca0c225b.tar.xz
os,internal/poll: support I/O on overlapped files not added to the poller
This fixes the support for I/O on overlapped files that are not added to the poller. Note that CL 661795 already added support for that, but it really only worked for pipes, not for plain files. Additionally, this CL also makes this kind of I/O operations to not notify the external poller to avoid confusing it. Updates #15388. Change-Id: I15c6ea74f3a87960aef0986598077b6eab9b9c99 Reviewed-on: https://go-review.googlesource.com/c/go/+/664415 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com> Reviewed-by: Alex Brainman <alex.brainman@gmail.com> Reviewed-by: Damien Neil <dneil@google.com> Auto-Submit: Quim Muntal <quimmuntal@gmail.com>
Diffstat (limited to 'src/internal')
-rw-r--r--src/internal/poll/fd_windows.go36
1 files changed, 26 insertions, 10 deletions
diff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go
index 99891de763..469d078fa3 100644
--- a/src/internal/poll/fd_windows.go
+++ b/src/internal/poll/fd_windows.go
@@ -68,7 +68,7 @@ var InitWSA = sync.OnceFunc(func() {
// operation contains superset of data necessary to perform all async IO.
type operation struct {
// Used by IOCP interface, it must be first field
- // of the struct, as our code rely on it.
+ // of the struct, as our code relies on it.
o syscall.Overlapped
// fields used by runtime.netpoll
@@ -88,6 +88,16 @@ type operation struct {
bufs []syscall.WSABuf
}
+func (o *operation) setEvent() {
+ h, err := windows.CreateEvent(nil, 0, 0, nil)
+ if err != nil {
+ // This shouldn't happen when all CreateEvent arguments are zero.
+ panic(err)
+ }
+ // Set the low bit so that the external IOCP doesn't receive the completion packet.
+ o.o.HEvent = h | 1
+}
+
func (o *operation) overlapped() *syscall.Overlapped {
if o.fd.isBlocking {
// Don't return the overlapped object if the file handle
@@ -155,11 +165,15 @@ func (o *operation) InitMsg(p []byte, oob []byte) {
// waitIO waits for the IO operation o to complete.
func waitIO(o *operation) error {
+ if o.fd.isBlocking {
+ panic("can't wait on blocking operations")
+ }
fd := o.fd
if !fd.pd.pollable() {
// The overlapped handle is not added to the runtime poller,
- // the only way to wait for the IO to complete is block.
- _, err := syscall.WaitForSingleObject(fd.Sysfd, syscall.INFINITE)
+ // the only way to wait for the IO to complete is block until
+ // the overlapped event is signaled.
+ _, err := syscall.WaitForSingleObject(o.o.HEvent, syscall.INFINITE)
return err
}
// Wait for our request to complete.
@@ -202,11 +216,19 @@ func execIO(o *operation, submit func(o *operation) error) (int, error) {
return 0, err
}
// Start IO.
+ if !fd.isBlocking && o.o.HEvent == 0 && !fd.pd.pollable() {
+ // If the handle is opened for overlapped IO but we can't
+ // use the runtime poller, then we need to use an
+ // event to wait for the IO to complete.
+ o.setEvent()
+ }
o.qty = 0
o.flags = 0
err = submit(o)
var waitErr error
- if err == syscall.ERROR_IO_PENDING || (err == nil && !o.fd.skipSyncNotif) {
+ // Blocking operations shouldn't return ERROR_IO_PENDING.
+ // Continue without waiting if that happens.
+ if !o.fd.isBlocking && (err == syscall.ERROR_IO_PENDING || (err == nil && !o.fd.skipSyncNotif)) {
// IO started asynchronously or completed synchronously but
// a sync notification is required. Wait for it to complete.
waitErr = waitIO(o)
@@ -345,11 +367,6 @@ func (fd *FD) initIO() error {
// so it is safe to add handles owned by the caller.
fd.initIOErr = fd.pd.init(fd)
if fd.initIOErr != nil {
- // This can happen if the handle is already associated
- // with another IOCP or if the isBlocking flag is incorrect.
- // In both cases, fallback to synchronous IO.
- fd.isBlocking = true
- fd.skipSyncNotif = true
return
}
fd.rop.runtimeCtx = fd.pd.runtimeCtx
@@ -389,7 +406,6 @@ func (fd *FD) Init(net string, pollable bool) error {
}
fd.isFile = fd.kind != kindNet
fd.isBlocking = !pollable
- fd.skipSyncNotif = fd.isBlocking
fd.rop.mode = 'r'
fd.wop.mode = 'w'
fd.rop.fd = fd