Source file src/internal/poll/fd_windows_test.go

     1  // Copyright 2017 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package poll_test
     6  
     7  import (
     8  	"errors"
     9  	"internal/poll"
    10  	"internal/syscall/windows"
    11  	"io"
    12  	"os"
    13  	"path/filepath"
    14  	"syscall"
    15  	"testing"
    16  	"unsafe"
    17  )
    18  
    19  func init() {
    20  	poll.InitWSA()
    21  }
    22  
    23  func TestWSASocketConflict(t *testing.T) {
    24  	t.Parallel()
    25  	s, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_OVERLAPPED)
    26  	if err != nil {
    27  		t.Fatal(err)
    28  	}
    29  	fd := poll.FD{Sysfd: s, IsStream: true, ZeroReadIsEOF: true}
    30  	if err = fd.Init("tcp", true); err != nil {
    31  		syscall.CloseHandle(s)
    32  		t.Fatal(err)
    33  	}
    34  	defer fd.Close()
    35  
    36  	const SIO_TCP_INFO = syscall.IOC_INOUT | syscall.IOC_VENDOR | 39
    37  	inbuf := uint32(0)
    38  	var outbuf _TCP_INFO_v0
    39  	cbbr := uint32(0)
    40  
    41  	var ov syscall.Overlapped
    42  	// Create an event so that we can efficiently wait for completion
    43  	// of a requested overlapped I/O operation.
    44  	ov.HEvent, _ = windows.CreateEvent(nil, 0, 0, nil)
    45  	if ov.HEvent == 0 {
    46  		t.Fatalf("could not create the event!")
    47  	}
    48  	defer syscall.CloseHandle(ov.HEvent)
    49  
    50  	if err = fd.WSAIoctl(
    51  		SIO_TCP_INFO,
    52  		(*byte)(unsafe.Pointer(&inbuf)),
    53  		uint32(unsafe.Sizeof(inbuf)),
    54  		(*byte)(unsafe.Pointer(&outbuf)),
    55  		uint32(unsafe.Sizeof(outbuf)),
    56  		&cbbr,
    57  		&ov,
    58  		0,
    59  	); err != nil && !errors.Is(err, syscall.ERROR_IO_PENDING) {
    60  		t.Fatalf("could not perform the WSAIoctl: %v", err)
    61  	}
    62  
    63  	if err != nil && errors.Is(err, syscall.ERROR_IO_PENDING) {
    64  		// It is possible that the overlapped I/O operation completed
    65  		// immediately so there is no need to wait for it to complete.
    66  		if res, err := syscall.WaitForSingleObject(ov.HEvent, syscall.INFINITE); res != 0 {
    67  			t.Fatalf("waiting for the completion of the overlapped IO failed: %v", err)
    68  		}
    69  	}
    70  }
    71  
    72  type _TCP_INFO_v0 struct {
    73  	State             uint32
    74  	Mss               uint32
    75  	ConnectionTimeMs  uint64
    76  	TimestampsEnabled bool
    77  	RttUs             uint32
    78  	MinRttUs          uint32
    79  	BytesInFlight     uint32
    80  	Cwnd              uint32
    81  	SndWnd            uint32
    82  	RcvWnd            uint32
    83  	RcvBuf            uint32
    84  	BytesOut          uint64
    85  	BytesIn           uint64
    86  	BytesReordered    uint32
    87  	BytesRetrans      uint32
    88  	FastRetrans       uint32
    89  	DupAcksIn         uint32
    90  	TimeoutEpisodes   uint32
    91  	SynRetrans        uint8
    92  }
    93  
    94  func newFD(t testing.TB, h syscall.Handle, kind string, overlapped bool) *poll.FD {
    95  	fd := poll.FD{
    96  		Sysfd:         h,
    97  		IsStream:      true,
    98  		ZeroReadIsEOF: true,
    99  	}
   100  	err := fd.Init(kind, overlapped)
   101  	if overlapped && err != nil {
   102  		// Overlapped file handles should not error.
   103  		fd.Close()
   104  		t.Fatal(err)
   105  	}
   106  	t.Cleanup(func() {
   107  		fd.Close()
   108  	})
   109  	return &fd
   110  }
   111  
   112  func newFile(t testing.TB, name string, overlapped bool) *poll.FD {
   113  	namep, err := syscall.UTF16PtrFromString(name)
   114  	if err != nil {
   115  		t.Fatal(err)
   116  	}
   117  	flags := syscall.FILE_ATTRIBUTE_NORMAL
   118  	if overlapped {
   119  		flags |= syscall.FILE_FLAG_OVERLAPPED
   120  	}
   121  	h, err := syscall.CreateFile(namep,
   122  		syscall.GENERIC_READ|syscall.GENERIC_WRITE,
   123  		syscall.FILE_SHARE_WRITE|syscall.FILE_SHARE_READ,
   124  		nil, syscall.OPEN_ALWAYS, uint32(flags), 0)
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  	typ, err := syscall.GetFileType(h)
   129  	if err != nil {
   130  		syscall.CloseHandle(h)
   131  		t.Fatal(err)
   132  	}
   133  	kind := "file"
   134  	if typ == syscall.FILE_TYPE_PIPE {
   135  		kind = "pipe"
   136  	}
   137  	return newFD(t, h, kind, overlapped)
   138  }
   139  
   140  func BenchmarkReadOverlapped(b *testing.B) {
   141  	benchmarkRead(b, true)
   142  }
   143  
   144  func BenchmarkReadSync(b *testing.B) {
   145  	benchmarkRead(b, false)
   146  }
   147  
   148  func benchmarkRead(b *testing.B, overlapped bool) {
   149  	name := filepath.Join(b.TempDir(), "foo")
   150  	const content = "hello world"
   151  	err := os.WriteFile(name, []byte(content), 0644)
   152  	if err != nil {
   153  		b.Fatal(err)
   154  	}
   155  	file := newFile(b, name, overlapped)
   156  	var buf [len(content)]byte
   157  	for b.Loop() {
   158  		_, err := io.ReadFull(file, buf[:])
   159  		if err != nil {
   160  			b.Fatal(err)
   161  		}
   162  		if _, err := file.Seek(0, io.SeekStart); err != nil {
   163  			b.Fatal(err)
   164  		}
   165  	}
   166  }
   167  

View as plain text