Source file
src/os/timeout_test.go
1
2
3
4
5
6
7 package os_test
8
9 import (
10 "fmt"
11 "io"
12 "math/rand"
13 "os"
14 "runtime"
15 "sync"
16 "testing"
17 "time"
18 )
19
20 func TestNonpollableDeadline(t *testing.T) {
21
22
23 if runtime.GOOS != "linux" && runtime.GOOS != "windows" {
24 t.Skipf("skipping on %s", runtime.GOOS)
25 }
26 t.Parallel()
27
28 f, err := os.CreateTemp("", "ostest")
29 if err != nil {
30 t.Fatal(err)
31 }
32 defer os.Remove(f.Name())
33 defer f.Close()
34 deadline := time.Now().Add(10 * time.Second)
35 if err := f.SetDeadline(deadline); err != os.ErrNoDeadline {
36 t.Errorf("SetDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
37 }
38 if err := f.SetReadDeadline(deadline); err != os.ErrNoDeadline {
39 t.Errorf("SetReadDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
40 }
41 if err := f.SetWriteDeadline(deadline); err != os.ErrNoDeadline {
42 t.Errorf("SetWriteDeadline on file returned %v, wanted %v", err, os.ErrNoDeadline)
43 }
44 }
45
46 type pipeDeadlineTest struct {
47 name string
48 create func(t *testing.T) (r, w *os.File)
49 }
50
51 var pipeDeadlinesTestCases []pipeDeadlineTest
52
53
54 var noDeadline time.Time
55
56 var readTimeoutTests = []struct {
57 timeout time.Duration
58 xerrs [2]error
59 }{
60
61
62 {-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
63
64 {50 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
65 }
66
67
68 func TestReadTimeout(t *testing.T) {
69 t.Parallel()
70
71 for _, tc := range pipeDeadlinesTestCases {
72 t.Run(tc.name, func(t *testing.T) {
73 t.Parallel()
74
75 r, w := tc.create(t)
76 defer r.Close()
77 defer w.Close()
78
79 if _, err := w.Write([]byte("READ TIMEOUT TEST")); err != nil {
80 t.Fatal(err)
81 }
82
83 for i, tt := range readTimeoutTests {
84 if err := r.SetReadDeadline(time.Now().Add(tt.timeout)); err != nil {
85 t.Fatalf("#%d: %v", i, err)
86 }
87 var b [1]byte
88 for j, xerr := range tt.xerrs {
89 for {
90 n, err := r.Read(b[:])
91 if xerr != nil {
92 if !isDeadlineExceeded(err) {
93 t.Fatalf("#%d/%d: %v", i, j, err)
94 }
95 }
96 if err == nil {
97 time.Sleep(tt.timeout / 3)
98 continue
99 }
100 if n != 0 {
101 t.Fatalf("#%d/%d: read %d; want 0", i, j, n)
102 }
103 break
104 }
105 }
106 }
107 })
108 }
109 }
110
111
112 func TestReadTimeoutMustNotReturn(t *testing.T) {
113 t.Parallel()
114
115 for _, tc := range pipeDeadlinesTestCases {
116 t.Run(tc.name, func(t *testing.T) {
117 t.Parallel()
118
119 r, w := tc.create(t)
120 defer r.Close()
121 defer w.Close()
122
123 max := time.NewTimer(100 * time.Millisecond)
124 defer max.Stop()
125 ch := make(chan error)
126 go func() {
127 if err := r.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil {
128 t.Error(err)
129 }
130 if err := r.SetWriteDeadline(time.Now().Add(-5 * time.Second)); err != nil {
131 t.Error(err)
132 }
133 if err := r.SetReadDeadline(noDeadline); err != nil {
134 t.Error(err)
135 }
136 var b [1]byte
137 _, err := r.Read(b[:])
138 ch <- err
139 }()
140
141 select {
142 case err := <-ch:
143 t.Fatalf("expected Read to not return, but it returned with %v", err)
144 case <-max.C:
145 w.Close()
146 err := <-ch
147 if os.IsTimeout(err) {
148 t.Fatal(err)
149 }
150 }
151 })
152 }
153 }
154
155 var writeTimeoutTests = []struct {
156 timeout time.Duration
157 xerrs [2]error
158 }{
159
160
161 {-5 * time.Second, [2]error{os.ErrDeadlineExceeded, os.ErrDeadlineExceeded}},
162
163 {10 * time.Millisecond, [2]error{nil, os.ErrDeadlineExceeded}},
164 }
165
166
167 func TestWriteTimeout(t *testing.T) {
168 t.Parallel()
169
170 for _, tc := range pipeDeadlinesTestCases {
171 t.Run(tc.name, func(t *testing.T) {
172 t.Parallel()
173
174 for i, tt := range writeTimeoutTests {
175 t.Run(fmt.Sprintf("#%d", i), func(t *testing.T) {
176 r, w := tc.create(t)
177 defer r.Close()
178 defer w.Close()
179
180 if err := w.SetWriteDeadline(time.Now().Add(tt.timeout)); err != nil {
181 t.Fatalf("%v", err)
182 }
183 for j, xerr := range tt.xerrs {
184 for {
185 n, err := w.Write([]byte("WRITE TIMEOUT TEST"))
186 if xerr != nil {
187 if !isDeadlineExceeded(err) {
188 t.Fatalf("%d: %v", j, err)
189 }
190 }
191 if err == nil {
192 time.Sleep(tt.timeout / 3)
193 continue
194 }
195 if n != 0 {
196 t.Fatalf("%d: wrote %d; want 0", j, n)
197 }
198 break
199 }
200 }
201 })
202 }
203 })
204 }
205 }
206
207
208 func TestWriteTimeoutMustNotReturn(t *testing.T) {
209 t.Parallel()
210
211 for _, tc := range pipeDeadlinesTestCases {
212 t.Run(tc.name, func(t *testing.T) {
213 t.Parallel()
214
215 r, w := tc.create(t)
216 defer r.Close()
217 defer w.Close()
218
219 max := time.NewTimer(100 * time.Millisecond)
220 defer max.Stop()
221 ch := make(chan error)
222 go func() {
223 if err := w.SetDeadline(time.Now().Add(-5 * time.Second)); err != nil {
224 t.Error(err)
225 }
226 if err := w.SetReadDeadline(time.Now().Add(-5 * time.Second)); err != nil {
227 t.Error(err)
228 }
229 if err := w.SetWriteDeadline(noDeadline); err != nil {
230 t.Error(err)
231 }
232 var b [1]byte
233 for {
234 if _, err := w.Write(b[:]); err != nil {
235 ch <- err
236 break
237 }
238 }
239 }()
240
241 select {
242 case err := <-ch:
243 t.Fatalf("expected Write to not return, but it returned with %v", err)
244 case <-max.C:
245 r.Close()
246 err := <-ch
247 if os.IsTimeout(err) {
248 t.Fatal(err)
249 }
250 }
251 })
252 }
253 }
254
255 const (
256
257
258
259
260
261
262 minDynamicTimeout = 1 * time.Millisecond
263
264
265
266
267
268
269
270 maxDynamicTimeout = 4 * time.Second
271 )
272
273
274
275 func timeoutUpperBound(d time.Duration) time.Duration {
276 switch runtime.GOOS {
277 case "openbsd", "netbsd":
278
279
280
281
282
283
284
285 return d * 3 / 2
286 }
287
288
289 return d * 11 / 10
290 }
291
292
293
294 func nextTimeout(actual time.Duration) (next time.Duration, ok bool) {
295 if actual >= maxDynamicTimeout {
296 return maxDynamicTimeout, false
297 }
298
299
300
301 next = actual * 5 / 4
302 return min(next, maxDynamicTimeout), true
303 }
304
305
306 func TestReadTimeoutFluctuation(t *testing.T) {
307 t.Parallel()
308
309 for _, tc := range pipeDeadlinesTestCases {
310 t.Run(tc.name, func(t *testing.T) {
311 t.Parallel()
312
313 r, w := tc.create(t)
314 defer r.Close()
315 defer w.Close()
316
317 d := minDynamicTimeout
318 b := make([]byte, 256)
319 for {
320 t.Logf("SetReadDeadline(+%v)", d)
321 t0 := time.Now()
322 deadline := t0.Add(d)
323 if err := r.SetReadDeadline(deadline); err != nil {
324 t.Fatalf("SetReadDeadline(%v): %v", deadline, err)
325 }
326 var n int
327 n, err := r.Read(b)
328 t1 := time.Now()
329
330 if n != 0 || err == nil || !isDeadlineExceeded(err) {
331 t.Errorf("Read did not return (0, timeout): (%d, %v)", n, err)
332 }
333
334 actual := t1.Sub(t0)
335 if t1.Before(deadline) {
336 t.Errorf("Read took %s; expected at least %s", actual, d)
337 }
338 if t.Failed() {
339 return
340 }
341 if want := timeoutUpperBound(d); actual > want {
342 next, ok := nextTimeout(actual)
343 if !ok {
344 t.Fatalf("Read took %s; expected at most %v", actual, want)
345 }
346
347
348 t.Logf("Read took %s (expected %s); trying with longer timeout", actual, d)
349 d = next
350 continue
351 }
352
353 break
354 }
355 })
356 }
357 }
358
359
360 func TestWriteTimeoutFluctuation(t *testing.T) {
361 t.Parallel()
362
363 for _, tc := range pipeDeadlinesTestCases {
364 t.Run(tc.name, func(t *testing.T) {
365 t.Parallel()
366
367 r, w := tc.create(t)
368 defer r.Close()
369 defer w.Close()
370
371 d := minDynamicTimeout
372 for {
373 t.Logf("SetWriteDeadline(+%v)", d)
374 t0 := time.Now()
375 deadline := t0.Add(d)
376 if err := w.SetWriteDeadline(deadline); err != nil {
377 t.Fatalf("SetWriteDeadline(%v): %v", deadline, err)
378 }
379 var n int64
380 var err error
381 for {
382 var dn int
383 dn, err = w.Write([]byte("TIMEOUT TRANSMITTER"))
384 n += int64(dn)
385 if err != nil {
386 break
387 }
388 }
389 t1 := time.Now()
390
391 if !isDeadlineExceeded(err) {
392 t.Fatalf("Write did not return (any, timeout): (%d, %v)", n, err)
393 }
394
395 actual := t1.Sub(t0)
396 if t1.Before(deadline) {
397 t.Errorf("Write took %s; expected at least %s", actual, d)
398 }
399 if t.Failed() {
400 return
401 }
402 if want := timeoutUpperBound(d); actual > want {
403 if n > 0 {
404
405
406
407
408 t.Logf("Wrote %d bytes into send buffer; retrying until buffer is full", n)
409 if d <= maxDynamicTimeout/2 {
410
411
412
413 d *= 2
414 }
415 } else if next, ok := nextTimeout(actual); !ok {
416 t.Fatalf("Write took %s; expected at most %s", actual, want)
417 } else {
418
419
420 t.Logf("Write took %s (expected %s); trying with longer timeout", actual, d)
421 d = next
422 }
423 continue
424 }
425
426 break
427 }
428 })
429 }
430 }
431
432
433 func TestVariousDeadlines(t *testing.T) {
434 t.Parallel()
435 for _, tc := range pipeDeadlinesTestCases {
436 t.Run(tc.name, func(t *testing.T) {
437 t.Parallel()
438 testVariousDeadlines(t, tc.create)
439 })
440 }
441 }
442
443
444 func TestVariousDeadlines1Proc(t *testing.T) {
445
446 if testing.Short() {
447 t.Skip("skipping in short mode")
448 }
449 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1))
450 for _, tc := range pipeDeadlinesTestCases {
451 t.Run(tc.name, func(t *testing.T) {
452 t.Parallel()
453 testVariousDeadlines(t, tc.create)
454 })
455 }
456 }
457
458
459 func TestVariousDeadlines4Proc(t *testing.T) {
460
461 if testing.Short() {
462 t.Skip("skipping in short mode")
463 }
464 defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(4))
465 for _, tc := range pipeDeadlinesTestCases {
466 t.Run(tc.name, func(t *testing.T) {
467 t.Parallel()
468 testVariousDeadlines(t, tc.create)
469 })
470 }
471 }
472
473 type neverEnding byte
474
475 func (b neverEnding) Read(p []byte) (int, error) {
476 for i := range p {
477 p[i] = byte(b)
478 }
479 return len(p), nil
480 }
481
482 func testVariousDeadlines(t *testing.T, create func(t *testing.T) (r, w *os.File)) {
483 type result struct {
484 n int64
485 err error
486 d time.Duration
487 }
488
489 handler := func(w *os.File, pasvch chan result) {
490
491
492 t0 := time.Now()
493 n, err := io.Copy(w, neverEnding('a'))
494 dt := time.Since(t0)
495 pasvch <- result{n, err, dt}
496 }
497
498 for _, timeout := range []time.Duration{
499 1 * time.Nanosecond,
500 2 * time.Nanosecond,
501 5 * time.Nanosecond,
502 50 * time.Nanosecond,
503 100 * time.Nanosecond,
504 200 * time.Nanosecond,
505 500 * time.Nanosecond,
506 750 * time.Nanosecond,
507 1 * time.Microsecond,
508 5 * time.Microsecond,
509 25 * time.Microsecond,
510 250 * time.Microsecond,
511 500 * time.Microsecond,
512 1 * time.Millisecond,
513 5 * time.Millisecond,
514 100 * time.Millisecond,
515 250 * time.Millisecond,
516 500 * time.Millisecond,
517 1 * time.Second,
518 } {
519 numRuns := 3
520 if testing.Short() {
521 numRuns = 1
522 if timeout > 500*time.Microsecond {
523 continue
524 }
525 }
526 for run := 0; run < numRuns; run++ {
527 t.Run(fmt.Sprintf("%v-%d", timeout, run+1), func(t *testing.T) {
528 r, w := create(t)
529 defer r.Close()
530 defer w.Close()
531
532 pasvch := make(chan result)
533 go handler(w, pasvch)
534
535 tooLong := 5 * time.Second
536 max := time.NewTimer(tooLong)
537 defer max.Stop()
538 actvch := make(chan result)
539 go func() {
540 t0 := time.Now()
541 if err := r.SetDeadline(t0.Add(timeout)); err != nil {
542 t.Error(err)
543 }
544 n, err := io.Copy(io.Discard, r)
545 dt := time.Since(t0)
546 r.Close()
547 actvch <- result{n, err, dt}
548 }()
549
550 select {
551 case res := <-actvch:
552 if isDeadlineExceeded(res.err) {
553 t.Logf("good client timeout after %v, reading %d bytes", res.d, res.n)
554 } else {
555 t.Fatalf("client Copy = %d, %v; want timeout", res.n, res.err)
556 }
557 case <-max.C:
558 t.Fatalf("timeout (%v) waiting for client to timeout (%v) reading", tooLong, timeout)
559 }
560
561 select {
562 case res := <-pasvch:
563 t.Logf("writer in %v wrote %d: %v", res.d, res.n, res.err)
564 case <-max.C:
565 t.Fatalf("timeout waiting for writer to finish writing")
566 }
567 })
568 }
569 }
570 }
571
572
573 func TestReadWriteDeadlineRace(t *testing.T) {
574 t.Parallel()
575
576 N := 1000
577 if testing.Short() {
578 N = 50
579 }
580
581 for _, tc := range pipeDeadlinesTestCases {
582 t.Run(tc.name, func(t *testing.T) {
583 t.Parallel()
584
585 r, w := tc.create(t)
586 defer r.Close()
587 defer w.Close()
588
589 var wg sync.WaitGroup
590 wg.Add(3)
591 go func() {
592 defer wg.Done()
593 tic := time.NewTicker(2 * time.Microsecond)
594 defer tic.Stop()
595 for i := 0; i < N; i++ {
596 if err := r.SetReadDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
597 break
598 }
599 if err := w.SetWriteDeadline(time.Now().Add(2 * time.Microsecond)); err != nil {
600 break
601 }
602 <-tic.C
603 }
604 }()
605 go func() {
606 defer wg.Done()
607 var b [1]byte
608 for i := 0; i < N; i++ {
609 _, err := r.Read(b[:])
610 if err != nil && !isDeadlineExceeded(err) {
611 t.Error("Read returned non-timeout error", err)
612 }
613 }
614 }()
615 go func() {
616 defer wg.Done()
617 var b [1]byte
618 for i := 0; i < N; i++ {
619 _, err := w.Write(b[:])
620 if err != nil && !isDeadlineExceeded(err) {
621 t.Error("Write returned non-timeout error", err)
622 }
623 }
624 }()
625 wg.Wait()
626 })
627 }
628 }
629
630
631
632 func TestRacyRead(t *testing.T) {
633 t.Parallel()
634
635 for _, tc := range pipeDeadlinesTestCases {
636 t.Run(tc.name, func(t *testing.T) {
637 t.Parallel()
638
639 r, w := tc.create(t)
640 defer r.Close()
641 defer w.Close()
642
643 var wg sync.WaitGroup
644 defer wg.Wait()
645
646 go io.Copy(w, rand.New(rand.NewSource(0)))
647
648 r.SetReadDeadline(time.Now().Add(time.Millisecond))
649 for i := 0; i < 10; i++ {
650 wg.Add(1)
651 go func() {
652 defer wg.Done()
653
654 b1 := make([]byte, 1024)
655 b2 := make([]byte, 1024)
656 for j := 0; j < 100; j++ {
657 _, err := r.Read(b1)
658 copy(b1, b2)
659 if err != nil {
660 if !isDeadlineExceeded(err) {
661 t.Error(err)
662 }
663 r.SetReadDeadline(time.Now().Add(time.Millisecond))
664 }
665 }
666 }()
667 }
668 })
669 }
670 }
671
672
673
674 func TestRacyWrite(t *testing.T) {
675 t.Parallel()
676
677 for _, tc := range pipeDeadlinesTestCases {
678 t.Run(tc.name, func(t *testing.T) {
679 t.Parallel()
680
681 r, w := tc.create(t)
682 defer r.Close()
683 defer w.Close()
684
685 var wg sync.WaitGroup
686 defer wg.Wait()
687
688 go io.Copy(io.Discard, r)
689
690 w.SetWriteDeadline(time.Now().Add(time.Millisecond))
691 for i := 0; i < 10; i++ {
692 wg.Add(1)
693 go func() {
694 defer wg.Done()
695
696 b1 := make([]byte, 1024)
697 b2 := make([]byte, 1024)
698 for j := 0; j < 100; j++ {
699 _, err := w.Write(b1)
700 copy(b1, b2)
701 if err != nil {
702 if !isDeadlineExceeded(err) {
703 t.Error(err)
704 }
705 w.SetWriteDeadline(time.Now().Add(time.Millisecond))
706 }
707 }
708 }()
709 }
710 })
711 }
712 }
713
View as plain text