From f414dfe4f5049c2c8998b4e6b90dee7fca0c225b Mon Sep 17 00:00:00 2001 From: qmuntal Date: Thu, 10 Apr 2025 16:03:46 +0200 Subject: 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 Reviewed-by: Michael Pratt Reviewed-by: Alex Brainman Reviewed-by: Damien Neil Auto-Submit: Quim Muntal --- src/internal/poll/fd_windows.go | 36 ++++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) (limited to 'src/internal') 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 -- cgit v1.3-5-g9baa