aboutsummaryrefslogtreecommitdiff
path: root/src/os/os_windows_test.go
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/os/os_windows_test.go
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/os/os_windows_test.go')
-rw-r--r--src/os/os_windows_test.go90
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)
+ }
}