| // Copyright 2017 The Go Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style | |
| // license that can be found in the LICENSE file. | |
| package poll_test | |
| import ( | |
| "errors" | |
| "internal/poll" | |
| "internal/syscall/windows" | |
| "io" | |
| "os" | |
| "path/filepath" | |
| "syscall" | |
| "testing" | |
| "unsafe" | |
| ) | |
| func init() { | |
| poll.InitWSA() | |
| } | |
| func TestWSASocketConflict(t *testing.T) { | |
| t.Parallel() | |
| s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_OVERLAPPED) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| fd := poll.FD{Sysfd: s, IsStream: true, ZeroReadIsEOF: true} | |
| if err = fd.Init("tcp", new(true)); err != nil { | |
| syscall.Closesocket(s) | |
| t.Fatal(err) | |
| } | |
| defer fd.Close() | |
| const SIO_TCP_INFO = syscall.IOC_INOUT | syscall.IOC_VENDOR | 39 | |
| inbuf := uint32(0) | |
| var outbuf _TCP_INFO_v0 | |
| cbbr := uint32(0) | |
| var ov syscall.Overlapped | |
| // Create an event so that we can efficiently wait for completion | |
| // of a requested overlapped I/O operation. | |
| ov.HEvent, _ = windows.CreateEvent(nil, 0, 0, nil) | |
| if ov.HEvent == 0 { | |
| t.Fatalf("could not create the event!") | |
| } | |
| defer syscall.CloseHandle(ov.HEvent) | |
| if err = fd.WSAIoctl( | |
| SIO_TCP_INFO, | |
| (*byte)(unsafe.Pointer(&inbuf)), | |
| uint32(unsafe.Sizeof(inbuf)), | |
| (*byte)(unsafe.Pointer(&outbuf)), | |
| uint32(unsafe.Sizeof(outbuf)), | |
| &cbbr, | |
| &ov, | |
| 0, | |
| ); err != nil && !errors.Is(err, syscall.ERROR_IO_PENDING) { | |
| t.Fatalf("could not perform the WSAIoctl: %v", err) | |
| } | |
| if err != nil && errors.Is(err, syscall.ERROR_IO_PENDING) { | |
| // It is possible that the overlapped I/O operation completed | |
| // immediately so there is no need to wait for it to complete. | |
| if res, err := syscall.WaitForSingleObject(ov.HEvent, syscall.INFINITE); res != 0 { | |
| t.Fatalf("waiting for the completion of the overlapped IO failed: %v", err) | |
| } | |
| } | |
| } | |
| type _TCP_INFO_v0 struct { | |
| State uint32 | |
| Mss uint32 | |
| ConnectionTimeMs uint64 | |
| TimestampsEnabled bool | |
| RttUs uint32 | |
| MinRttUs uint32 | |
| BytesInFlight uint32 | |
| Cwnd uint32 | |
| SndWnd uint32 | |
| RcvWnd uint32 | |
| RcvBuf uint32 | |
| BytesOut uint64 | |
| BytesIn uint64 | |
| BytesReordered uint32 | |
| BytesRetrans uint32 | |
| FastRetrans uint32 | |
| DupAcksIn uint32 | |
| TimeoutEpisodes uint32 | |
| SynRetrans uint8 | |
| } | |
| func newFD(t testing.TB, h syscall.Handle, kind string, overlapped bool) *poll.FD { | |
| fd := poll.FD{ | |
| Sysfd: h, | |
| IsStream: true, | |
| ZeroReadIsEOF: true, | |
| } | |
| err := fd.Init(kind, &overlapped) | |
| if overlapped && err != nil { | |
| // Overlapped file handles should not error. | |
| fd.Close() | |
| t.Fatal(err) | |
| } | |
| t.Cleanup(func() { | |
| fd.Close() | |
| }) | |
| return &fd | |
| } | |
| func newFile(t testing.TB, name string, overlapped bool) *poll.FD { | |
| namep, err := syscall.UTF16PtrFromString(name) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| flags := syscall.FILE_ATTRIBUTE_NORMAL | |
| if overlapped { | |
| flags |= syscall.FILE_FLAG_OVERLAPPED | |
| } | |
| h, err := syscall.CreateFile(namep, | |
| syscall.GENERIC_READ|syscall.GENERIC_WRITE, | |
| syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_READ, | |
| nil, syscall.OPEN_ALWAYS, uint32(flags), 0) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| typ, err := syscall.GetFileType(h) | |
| if err != nil { | |
| syscall.CloseHandle(h) | |
| t.Fatal(err) | |
| } | |
| kind := "file" | |
| if typ == syscall.FILE_TYPE_PIPE { | |
| kind = "pipe" | |
| } | |
| return newFD(t, h, kind, overlapped) | |
| } | |
| func TestFileSkipsCompletionPortOnSuccess(t *testing.T) { | |
| t.Parallel() | |
| fd := newFile(t, filepath.Join(t.TempDir(), "foo"), true) | |
| if !poll.SkipsCompletionPortOnSuccess(fd) { | |
| t.Fatal("expected file handles to skip completion port on success") | |
| } | |
| } | |
| func TestSocketSkipsCompletionPortOnSuccess(t *testing.T) { | |
| // Assume that all Windows we test on only have IFS handles for TCP sockets. | |
| t.Parallel() | |
| s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_OVERLAPPED) | |
| if err != nil { | |
| t.Fatal(err) | |
| } | |
| fd := newFD(t, s, "tcp", true) | |
| if !poll.SkipsCompletionPortOnSuccess(fd) { | |
| t.Fatal("expected socket handles to skip completion port on success") | |
| } | |
| } | |
| func BenchmarkReadOverlapped(b *testing.B) { | |
| benchmarkRead(b, true) | |
| } | |
| func BenchmarkReadSync(b *testing.B) { | |
| benchmarkRead(b, false) | |
| } | |
| func benchmarkRead(b *testing.B, overlapped bool) { | |
| name := filepath.Join(b.TempDir(), "foo") | |
| const content = "hello world" | |
| err := os.WriteFile(name, []byte(content), 0644) | |
| if err != nil { | |
| b.Fatal(err) | |
| } | |
| file := newFile(b, name, overlapped) | |
| var buf [len(content)]byte | |
| for b.Loop() { | |
| _, err := io.ReadFull(file, buf[:]) | |
| if err != nil { | |
| b.Fatal(err) | |
| } | |
| if _, err := file.Seek(0, io.SeekStart); err != nil { | |
| b.Fatal(err) | |
| } | |
| } | |
| } |