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/os | |
| 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/os')
| -rw-r--r-- | src/os/os_windows_test.go | 90 |
1 files changed, 76 insertions, 14 deletions
diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go index 5fbf987291..15f1b616e6 100644 --- a/src/os/os_windows_test.go +++ b/src/os/os_windows_test.go @@ -1984,31 +1984,93 @@ func TestPipeCanceled(t *testing.T) { } } -func TestPipeExternalIOCP(t *testing.T) { +func iocpAssociateFile(f *os.File, iocp syscall.Handle) error { + sc, err := f.SyscallConn() + if err != nil { + return err + } + var syserr error + err = sc.Control(func(fd uintptr) { + if _, err = windows.CreateIoCompletionPort(syscall.Handle(fd), iocp, 0, 0); err != nil { + syserr = err + } + }) + if err == nil { + err = syserr + } + return err +} + +func TestFileAssociatedWithExternalIOCP(t *testing.T) { // Test that a caller can associate an overlapped handle to an external IOCP - // even when the handle is also associated to a poll.FD. Also test that - // the FD can still perform I/O after the association. + // after the handle has been passed to os.NewFile. + // Also test that the File can perform I/O after it is associated with the + // external IOCP and that those operations do not post to the external IOCP. t.Parallel() name := pipeName() pipe := newMessagePipe(t, name, true) - _ = newFileOverlapped(t, name, true) // Just open a pipe client + _ = newFileOverlapped(t, name, true) // just open a pipe client + + // Use a file to exercise WriteAt. + file := newFileOverlapped(t, filepath.Join(t.TempDir(), "a"), true) - sc, err := pipe.SyscallConn() + iocp, err := windows.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0) if err != nil { - t.Error(err) - return + t.Fatal(err) } - if err := sc.Control(func(fd uintptr) { - _, err := windows.CreateIoCompletionPort(syscall.Handle(fd), 0, 0, 1) - if err != nil { + defer func() { + if iocp == syscall.InvalidHandle { + // Already closed at the end of the test. + return + } + if err := syscall.CloseHandle(iocp); err != nil { t.Fatal(err) } - }); err != nil { - t.Error(err) + }() + + ch := make(chan error, 1) + go func() { + var bytes, key uint32 + var overlapped *syscall.Overlapped + err := syscall.GetQueuedCompletionStatus(syscall.Handle(iocp), &bytes, &key, &overlapped, syscall.INFINITE) + ch <- err + }() + + if err := iocpAssociateFile(pipe, iocp); err != nil { + t.Fatal(err) + } + if err := iocpAssociateFile(file, iocp); err != nil { + t.Fatal(err) } - _, err = pipe.Write([]byte("hello")) - if err != nil { + if _, err := pipe.Write([]byte("hello")); err != nil { + t.Fatal(err) + } + if _, err := file.Write([]byte("hello")); err != nil { t.Fatal(err) } + if _, err := file.WriteAt([]byte("hello"), 0); err != nil { + t.Fatal(err) + } + + // Wait fot he goroutine to call GetQueuedCompletionStatus. + time.Sleep(100 * time.Millisecond) + + // Trigger ERROR_ABANDONED_WAIT_0. + if err := syscall.CloseHandle(iocp); err != nil { + t.Fatal(err) + } + + // Wait for the completion to be posted to the IOCP. + err = <-ch + iocp = syscall.InvalidHandle + const ERROR_ABANDONED_WAIT_0 = syscall.Errno(735) + switch err { + case ERROR_ABANDONED_WAIT_0: + // This is what we expect. + case nil: + t.Error("unexpected queued completion") + default: + t.Error(err) + } } |
