diff options
| author | qmuntal <quimmuntal@gmail.com> | 2025-04-10 16:03:46 +0200 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2025-04-14 05:40:53 -0700 |
| commit | f414dfe4f5049c2c8998b4e6b90dee7fca0c225b (patch) | |
| tree | 53107a6dc07e3d1321d2bc65e74a5f43c801f74d /src/internal | |
| parent | 13b7c7d8d21765886697c952ffbb7fb853a2bf9a (diff) | |
| download | go-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.go | 36 |
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 |
