Source file
src/os/newfile_windows_test.go
1
2
3
4
5 package os_test
6
7 import (
8 "errors"
9 "internal/poll"
10 "internal/syscall/windows"
11 "io"
12 "os"
13 "path/filepath"
14 "sync"
15 "syscall"
16 "testing"
17 "time"
18 )
19
20 func newLazyFile(t testing.TB, overlapped bool) (*os.File, syscall.Handle) {
21 t.Helper()
22 name := filepath.Join(t.TempDir(), "file")
23 if err := os.WriteFile(name, []byte("hello"), 0600); err != nil {
24 t.Fatal(err)
25 }
26 flags := syscall.O_RDWR
27 if overlapped {
28 flags |= windows.O_FILE_FLAG_OVERLAPPED
29 }
30 h := openHandle(t, name, flags)
31 f := os.NewFile(uintptr(h), name)
32 t.Cleanup(func() {
33 if err := f.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
34 t.Error(err)
35 }
36 })
37 return f, h
38 }
39
40 func openHandle(t testing.TB, name string, flags int) syscall.Handle {
41 t.Helper()
42 h, err := syscall.Open(name, flags|syscall.O_CLOEXEC, 0600)
43 if err != nil {
44 t.Fatal(err)
45 }
46 return h
47 }
48
49 func TestNewFileLazyInit(t *testing.T) {
50 t.Parallel()
51 for _, mode := range []string{"sync", "overlapped"} {
52 t.Run(mode, func(t *testing.T) {
53 for _, first := range []string{"read", "seek", "deadline"} {
54 t.Run(first, func(t *testing.T) {
55 t.Parallel()
56 f, _ := newLazyFile(t, mode == "overlapped")
57 var buf [5]byte
58 switch first {
59 case "read":
60 if n, err := f.Read(buf[:]); err != nil || n != len(buf) || string(buf[:]) != "hello" {
61 t.Fatalf("Read = %q, %d, %v", buf, n, err)
62 }
63 case "seek":
64 if off, err := f.Seek(1, io.SeekStart); err != nil || off != 1 {
65 t.Fatalf("Seek = %d, %v", off, err)
66 }
67 if n, err := f.Read(buf[:1]); err != nil || n != 1 || buf[0] != 'e' {
68 t.Fatalf("Read after Seek = %q, %d, %v", buf[:1], n, err)
69 }
70 case "deadline":
71 err := f.SetReadDeadline(time.Now().Add(-time.Second))
72 if mode == "sync" {
73 if !errors.Is(err, os.ErrNoDeadline) {
74 t.Fatalf("SetReadDeadline = %v; want ErrNoDeadline", err)
75 }
76 } else {
77 if err != nil {
78 t.Fatal(err)
79 }
80 if _, err := f.Read(buf[:1]); !errors.Is(err, os.ErrDeadlineExceeded) {
81 t.Fatalf("Read = %v; want ErrDeadlineExceeded", err)
82 }
83 }
84 }
85 })
86 }
87 })
88 }
89 }
90
91 func TestNewFileBlockedHandle(t *testing.T) {
92 t.Parallel()
93 for _, action := range []string{"close", "readClose", "deadlineControl"} {
94 t.Run(action, func(t *testing.T) {
95 t.Parallel()
96 name := pipeName()
97 writer := newBytePipe(t, name, true)
98 h := openHandle(t, name, syscall.O_RDWR)
99 var wg sync.WaitGroup
100 wg.Go(func() {
101 var buf [1]byte
102 var n uint32
103 syscall.ReadFile(h, buf[:], &n, nil)
104 })
105 time.Sleep(20 * time.Millisecond)
106 f := os.NewFile(uintptr(h), name)
107 defer func() {
108 writer.Close()
109 syscall.CancelIoEx(h, nil)
110 wg.Wait()
111 f.Close()
112 }()
113
114 if action == "readClose" {
115 wg.Go(func() {
116 var buf [1]byte
117 _, err := f.Read(buf[:])
118 if !errors.Is(err, os.ErrClosed) {
119 t.Errorf("Read = %v; want ErrClosed", err)
120 }
121 })
122 time.Sleep(20 * time.Millisecond)
123 }
124 if action == "deadlineControl" {
125
126
127 wg.Go(func() {
128 if err := f.SetDeadline(time.Time{}); !errors.Is(err, os.ErrNoDeadline) {
129 t.Errorf("SetDeadline = %v; want ErrNoDeadline", err)
130 }
131 })
132 time.Sleep(20 * time.Millisecond)
133 }
134 if action == "close" || action == "readClose" {
135 if err := f.Close(); err != nil {
136 t.Fatal(err)
137 }
138 return
139 }
140 if got := f.Fd(); got != uintptr(h) {
141 t.Errorf("Fd = %d; want %d", got, h)
142 }
143 raw, err := f.SyscallConn()
144 if err == nil {
145 err = raw.Control(func(fd uintptr) {
146 if err := syscall.CancelIoEx(syscall.Handle(fd), nil); err != nil && err != syscall.ERROR_NOT_FOUND {
147 t.Error(err)
148 }
149 })
150 }
151 if err != nil {
152 t.Fatal(err)
153 }
154 })
155 }
156 }
157
158 func TestNewFileEventFallback(t *testing.T) {
159 t.Parallel()
160 for _, fallback := range []string{"fd", "externalIOCP"} {
161 t.Run(fallback, func(t *testing.T) {
162 t.Parallel()
163 f, h := newLazyFile(t, true)
164 if fallback == "fd" {
165 if got := f.Fd(); got != uintptr(h) {
166 t.Fatalf("Fd = %d; want %d", got, h)
167 }
168 } else {
169 iocp, err := windows.CreateIoCompletionPort(syscall.InvalidHandle, 0, 0, 0)
170 if err != nil {
171 t.Fatal(err)
172 }
173 defer syscall.CloseHandle(iocp)
174 if _, err := windows.CreateIoCompletionPort(h, iocp, 0, 0); err != nil {
175 t.Fatal(err)
176 }
177 }
178 if err := f.SetDeadline(time.Time{}); !errors.Is(err, os.ErrNoDeadline) {
179 t.Fatalf("SetDeadline = %v; want ErrNoDeadline", err)
180 }
181 var buf [5]byte
182 if n, err := f.ReadAt(buf[:], 0); err != nil || n != len(buf) || string(buf[:]) != "hello" {
183 t.Fatalf("ReadAt = %q, %d, %v", buf, n, err)
184 }
185 })
186 }
187 }
188
189 func TestNewFileLazyInitRace(t *testing.T) {
190 t.Parallel()
191 for _, action := range []string{"close", "fd"} {
192 t.Run(action, func(t *testing.T) {
193 for range 100 {
194 f, _ := newLazyFile(t, true)
195 start := make(chan struct{})
196 var wg sync.WaitGroup
197 for _, op := range []func() error{
198 func() error {
199 var b [1]byte
200 _, err := f.ReadAt(b[:], 0)
201 return err
202 },
203 func() error { return f.SetDeadline(time.Time{}) },
204 } {
205 wg.Go(func() {
206 <-start
207 if err := op(); err != nil && !errors.Is(err, os.ErrClosed) && !errors.Is(err, os.ErrNoDeadline) && err != poll.ErrFileClosing {
208 t.Error(err)
209 }
210 })
211 }
212 wg.Go(func() {
213 <-start
214 if action == "close" {
215 if err := f.Close(); err != nil {
216 t.Error(err)
217 }
218 } else {
219 f.Fd()
220 }
221 })
222 close(start)
223 wg.Wait()
224 f.Close()
225 }
226 })
227 }
228 }
229
View as plain text