@@ -643,9 +643,24 @@ func TestWriteToTimeout(t *testing.T) {
643
643
}
644
644
}
645
645
646
- func TestReadTimeoutFluctuation (t * testing.T ) {
647
- t .Parallel ()
646
+ const (
647
+ // minDynamicTimeout is the minimum timeout to attempt for
648
+ // tests that automatically increase timeouts until success.
649
+ //
650
+ // Lower values may allow tests to succeed more quickly if the value is close
651
+ // to the true minimum, but may require more iterations (and waste more time
652
+ // and CPU power on failed attempts) if the timeout is too low.
653
+ minDynamicTimeout = 1 * time .Millisecond
654
+
655
+ // maxDynamicTimeout is the maximum timeout to attempt for
656
+ // tests that automatically increase timeouts until succeess.
657
+ //
658
+ // This should be a strict upper bound on the latency of the timeout: if a
659
+ // test would increase the timeout beyond this value, the test fails.
660
+ maxDynamicTimeout = 1 * time .Second
661
+ )
648
662
663
+ func TestReadTimeoutFluctuation (t * testing.T ) {
649
664
ln , err := newLocalListener ("tcp" )
650
665
if err != nil {
651
666
t .Fatal (err )
@@ -658,27 +673,52 @@ func TestReadTimeoutFluctuation(t *testing.T) {
658
673
}
659
674
defer c .Close ()
660
675
661
- max := time .NewTimer (time .Second )
662
- defer max .Stop ()
663
- ch := make (chan error )
664
- go timeoutReceiver (c , 100 * time .Millisecond , 50 * time .Millisecond , 250 * time .Millisecond , ch )
676
+ d := minDynamicTimeout
677
+ b := make ([]byte , 256 )
678
+ for {
679
+ t .Logf ("SetReadDeadline(+%v)" , d )
680
+ t0 := time .Now ()
681
+ deadline := t0 .Add (d )
682
+ if err = c .SetReadDeadline (deadline ); err != nil {
683
+ t .Fatalf ("SetReadDeadline(%v): %v" , deadline , err )
684
+ }
685
+ var n int
686
+ n , err = c .Read (b )
687
+ t1 := time .Now ()
665
688
666
- select {
667
- case <- max .C :
668
- t .Fatal ("Read took over 1s; expected 0.1s" )
669
- case err := <- ch :
689
+ if n != 0 || err == nil || ! err .(Error ).Timeout () {
690
+ t .Errorf ("Read did not return (0, timeout): (%d, %v)" , n , err )
691
+ }
670
692
if perr := parseReadError (err ); perr != nil {
671
693
t .Error (perr )
672
694
}
673
695
if ! isDeadlineExceeded (err ) {
674
- t .Fatal (err )
696
+ t .Errorf ("Read error is not DeadlineExceeded: %v" , err )
697
+ }
698
+
699
+ actual := t1 .Sub (t0 )
700
+ if t1 .Before (deadline ) {
701
+ t .Errorf ("Read took %s; expected at least %s" , actual , d )
675
702
}
703
+ if t .Failed () {
704
+ return
705
+ }
706
+ if actual > d * 11 / 10 {
707
+ if actual > maxDynamicTimeout || d > maxDynamicTimeout / 2 {
708
+ t .Fatalf ("Read took %s; expected %v" , actual , d )
709
+ }
710
+ // Maybe this machine is too slow to reliably schedule goroutines within
711
+ // the requested duration. Increase the timeout and try again.
712
+ t .Logf ("Read took %s (expected %s); trying with longer timeout" , actual , d )
713
+ d *= 2
714
+ continue
715
+ }
716
+
717
+ break
676
718
}
677
719
}
678
720
679
721
func TestReadFromTimeoutFluctuation (t * testing.T ) {
680
- t .Parallel ()
681
-
682
722
c1 , err := newLocalPacketListener ("udp" )
683
723
if err != nil {
684
724
t .Fatal (err )
@@ -691,27 +731,52 @@ func TestReadFromTimeoutFluctuation(t *testing.T) {
691
731
}
692
732
defer c2 .Close ()
693
733
694
- max := time .NewTimer (time .Second )
695
- defer max .Stop ()
696
- ch := make (chan error )
697
- go timeoutPacketReceiver (c2 .(PacketConn ), 100 * time .Millisecond , 50 * time .Millisecond , 250 * time .Millisecond , ch )
734
+ d := minDynamicTimeout
735
+ b := make ([]byte , 256 )
736
+ for {
737
+ t .Logf ("SetReadDeadline(+%v)" , d )
738
+ t0 := time .Now ()
739
+ deadline := t0 .Add (d )
740
+ if err = c2 .SetReadDeadline (deadline ); err != nil {
741
+ t .Fatalf ("SetReadDeadline(%v): %v" , deadline , err )
742
+ }
743
+ var n int
744
+ n , _ , err = c2 .(PacketConn ).ReadFrom (b )
745
+ t1 := time .Now ()
698
746
699
- select {
700
- case <- max .C :
701
- t .Fatal ("ReadFrom took over 1s; expected 0.1s" )
702
- case err := <- ch :
747
+ if n != 0 || err == nil || ! err .(Error ).Timeout () {
748
+ t .Errorf ("ReadFrom did not return (0, timeout): (%d, %v)" , n , err )
749
+ }
703
750
if perr := parseReadError (err ); perr != nil {
704
751
t .Error (perr )
705
752
}
706
753
if ! isDeadlineExceeded (err ) {
707
- t .Fatal ( err )
754
+ t .Errorf ( "ReadFrom error is not DeadlineExceeded: %v" , err )
708
755
}
756
+
757
+ actual := t1 .Sub (t0 )
758
+ if t1 .Before (deadline ) {
759
+ t .Errorf ("ReadFrom took %s; expected at least %s" , actual , d )
760
+ }
761
+ if t .Failed () {
762
+ return
763
+ }
764
+ if actual > d * 11 / 10 {
765
+ if actual > maxDynamicTimeout || d > maxDynamicTimeout / 2 {
766
+ t .Fatalf ("ReadFrom took %s; expected %s" , actual , d )
767
+ }
768
+ // Maybe this machine is too slow to reliably schedule goroutines within
769
+ // the requested duration. Increase the timeout and try again.
770
+ t .Logf ("ReadFrom took %s (expected %s); trying with longer timeout" , actual , d )
771
+ d *= 2
772
+ continue
773
+ }
774
+
775
+ break
709
776
}
710
777
}
711
778
712
779
func TestWriteTimeoutFluctuation (t * testing.T ) {
713
- t .Parallel ()
714
-
715
780
switch runtime .GOOS {
716
781
case "plan9" :
717
782
t .Skipf ("not supported on %s" , runtime .GOOS )
@@ -729,25 +794,64 @@ func TestWriteTimeoutFluctuation(t *testing.T) {
729
794
}
730
795
defer c .Close ()
731
796
732
- d := time .Second
733
- if iOS () {
734
- d = 3 * time .Second // see golang.org/issue/10775
735
- }
736
- max := time .NewTimer (d )
737
- defer max .Stop ()
738
- ch := make (chan error )
739
- go timeoutTransmitter (c , 100 * time .Millisecond , 50 * time .Millisecond , 250 * time .Millisecond , ch )
797
+ d := minDynamicTimeout
798
+ for {
799
+ t .Logf ("SetWriteDeadline(+%v)" , d )
800
+ t0 := time .Now ()
801
+ deadline := t0 .Add (d )
802
+ if err = c .SetWriteDeadline (deadline ); err != nil {
803
+ t .Fatalf ("SetWriteDeadline(%v): %v" , deadline , err )
804
+ }
805
+ var n int64
806
+ for {
807
+ var dn int
808
+ dn , err = c .Write ([]byte ("TIMEOUT TRANSMITTER" ))
809
+ n += int64 (dn )
810
+ if err != nil {
811
+ break
812
+ }
813
+ }
814
+ t1 := time .Now ()
740
815
741
- select {
742
- case <- max .C :
743
- t .Fatalf ("Write took over %v; expected 0.1s" , d )
744
- case err := <- ch :
816
+ if err == nil || ! err .(Error ).Timeout () {
817
+ t .Fatalf ("Write did not return (any, timeout): (%d, %v)" , n , err )
818
+ }
745
819
if perr := parseWriteError (err ); perr != nil {
746
820
t .Error (perr )
747
821
}
748
822
if ! isDeadlineExceeded (err ) {
749
- t .Fatal ( err )
823
+ t .Errorf ( "Write error is not DeadlineExceeded: %v" , err )
750
824
}
825
+
826
+ actual := t1 .Sub (t0 )
827
+ if t1 .Before (deadline ) {
828
+ t .Errorf ("Write took %s; expected at least %s" , actual , d )
829
+ }
830
+ if t .Failed () {
831
+ return
832
+ }
833
+ if actual > d * 11 / 10 {
834
+ if n > 0 {
835
+ // SetWriteDeadline specifies a time “after which I/O operations fail
836
+ // instead of blocking”. However, the kernel's send buffer is not yet
837
+ // full, we may be able to write some arbitrary (but finite) number of
838
+ // bytes to it without blocking.
839
+ t .Logf ("Wrote %d bytes into send buffer; retrying until buffer is full" , n )
840
+ if d <= maxDynamicTimeout / 2 {
841
+ d *= 2
842
+ }
843
+ } else if actual > maxDynamicTimeout || d > maxDynamicTimeout / 2 {
844
+ t .Fatalf ("Write took %s; expected %s" , actual , d )
845
+ } else {
846
+ // Maybe this machine is too slow to reliably schedule goroutines within
847
+ // the requested duration. Increase the timeout and try again.
848
+ t .Logf ("Write took %s (expected %s); trying with longer timeout" , actual , d )
849
+ d *= 2
850
+ }
851
+ continue
852
+ }
853
+
854
+ break
751
855
}
752
856
}
753
857
0 commit comments