aboutsummaryrefslogtreecommitdiff
path: root/src/os/os_windows_test.go
diff options
context:
space:
mode:
authorqmuntal <quimmuntal@gmail.com>2025-04-10 17:45:47 +0200
committerQuim Muntal <quimmuntal@gmail.com>2025-04-24 06:51:09 -0700
commit8a8f506516e1210c9ca3a352d76bd1d570c407fd (patch)
treeac3815d0347d06b817638779e7c2eb3dac61a5ee /src/os/os_windows_test.go
parentc1fc209c41c18806b7cef1cf114f1ca9b3731eb9 (diff)
downloadgo-8a8f506516e1210c9ca3a352d76bd1d570c407fd.tar.xz
os,internal/poll: disassociate handle from IOCP in File.Fd
Go 1.25 will gain support for overlapped IO on handles passed to os.NewFile thanks to CL 662236. It was previously not possible to add an overlapped handle to the Go runtime's IO completion port (IOCP), and now happens on the first call the an IO method. This means that there is code that relies on the fact that File.Fd returns a handle that can always be associated with a custom IOCP. That wouldn't be the case anymore, as a handle can only be associated with one IOCP at a time and it must be explicitly disassociated. To fix this breaking change, File.Fd will disassociate the handle from the Go runtime IOCP before returning it. It is then not necessary to defer the association until the first IO method is called, which was recently added in CL 661955 to support this same use case, but in a more complex and unreliable way. Updates #19098. Cq-Include-Trybots: luci.golang.try:gotip-windows-amd64-race,gotip-windows-amd64-longtest,gotip-windows-arm64 Change-Id: Id8a7e04d35057047c61d1733bad5bf45494b2c28 Reviewed-on: https://go-review.googlesource.com/c/go/+/664455 Reviewed-by: Damien Neil <dneil@google.com> Reviewed-by: Alex Brainman <alex.brainman@gmail.com> Reviewed-by: Michael Pratt <mpratt@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Diffstat (limited to 'src/os/os_windows_test.go')
-rw-r--r--src/os/os_windows_test.go62
1 files changed, 49 insertions, 13 deletions
diff --git a/src/os/os_windows_test.go b/src/os/os_windows_test.go
index d78080ccc4..89a61f0229 100644
--- a/src/os/os_windows_test.go
+++ b/src/os/os_windows_test.go
@@ -1985,19 +1985,7 @@ func TestPipeCanceled(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
- }
+ _, err := windows.CreateIoCompletionPort(syscall.Handle(f.Fd()), iocp, 0, 0)
return err
}
@@ -2075,6 +2063,54 @@ func TestFileAssociatedWithExternalIOCP(t *testing.T) {
}
}
+func TestFileWriteFdRace(t *testing.T) {
+ t.Parallel()
+
+ f := newFileOverlapped(t, filepath.Join(t.TempDir(), "a"), true)
+
+ var wg sync.WaitGroup
+ wg.Add(2)
+
+ go func() {
+ defer wg.Done()
+ n, err := f.Write([]byte("hi"))
+ if err != nil {
+ // We look at error strings as the
+ // expected errors are OS-specific.
+ switch {
+ case errors.Is(err, windows.ERROR_INVALID_HANDLE):
+ // Ignore an expected error.
+ default:
+ // Unexpected error.
+ t.Error(err)
+ }
+ return
+ }
+ if n != 2 {
+ t.Errorf("wrote %d bytes, expected 2", n)
+ return
+ }
+ }()
+ go func() {
+ defer wg.Done()
+ f.Fd()
+ }()
+ wg.Wait()
+
+ iocp, err := windows.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer syscall.CloseHandle(iocp)
+ if err := iocpAssociateFile(f, iocp); err != nil {
+ t.Fatal(err)
+ }
+
+ if _, err := f.Write([]byte("hi")); err != nil {
+ t.Error(err)
+ }
+}
+
func TestSplitPath(t *testing.T) {
t.Parallel()
for _, tt := range []struct{ path, wantDir, wantBase string }{