]> Cypherpunks.ru repositories - gostls13.git/commitdiff
context: fix a flaky timeout in TestLayersTimeout
authorBryan C. Mills <bcmills@google.com>
Mon, 30 Mar 2020 18:04:08 +0000 (14:04 -0400)
committerBryan C. Mills <bcmills@google.com>
Tue, 31 Mar 2020 17:59:03 +0000 (17:59 +0000)
In CL 223019, I reduced the short timeout in the testLayers helper to
be even shorter than it was. That exposed a racy (time-dependent)
select later in the function, which failed in one of the slower
builders (android-386-emu).

Also streamline the test to make it easier to test with a very high -count flag:
- Run tests that sleep for shortDuration in parallel to reduce latency.
- Use shorter durations in examples to reduce test running time.
- Avoid mutating global state (in package math/rand) in testLayers.

After this change (but not before it),
'go test -run=TestLayersTimeout -count=100000 context' passes on my workstation.

Fixes #38161

Change-Id: Iaf4abe7ac308b2100d8828267cda9f4f8ae4be82
Reviewed-on: https://go-review.googlesource.com/c/go/+/226457
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/context/context_test.go
src/context/example_test.go

index 98c6683335370fbd68f5e473c36e963dd81555e5..6b392a29da5ed3e4fa4ff2590431131e351caeb4 100644 (file)
@@ -27,6 +27,7 @@ type testingT interface {
        Log(args ...interface{})
        Logf(format string, args ...interface{})
        Name() string
+       Parallel()
        Skip(args ...interface{})
        SkipNow()
        Skipf(format string, args ...interface{})
@@ -284,6 +285,8 @@ func testDeadline(c Context, name string, t testingT) {
 }
 
 func XTestDeadline(t testingT) {
+       t.Parallel()
+
        c, _ := WithDeadline(Background(), time.Now().Add(shortDuration))
        if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
                t.Errorf("c.String() = %q want prefix %q", got, prefix)
@@ -307,6 +310,8 @@ func XTestDeadline(t testingT) {
 }
 
 func XTestTimeout(t testingT) {
+       t.Parallel()
+
        c, _ := WithTimeout(Background(), shortDuration)
        if got, prefix := fmt.Sprint(c), "context.Background.WithDeadline("; !strings.HasPrefix(got, prefix) {
                t.Errorf("c.String() = %q want prefix %q", got, prefix)
@@ -417,9 +422,9 @@ func XTestAllocs(t testingT, testingShort func() bool, testingAllocsPerRun func(
                        gccgoLimit: 3,
                },
                {
-                       desc: "WithTimeout(bg, 15*time.Millisecond)",
+                       desc: "WithTimeout(bg, 1*time.Nanosecond)",
                        f: func() {
-                               c, _ := WithTimeout(bg, 15*time.Millisecond)
+                               c, _ := WithTimeout(bg, 1*time.Nanosecond)
                                <-c.Done()
                        },
                        limit:      12,
@@ -545,7 +550,9 @@ func XTestLayersTimeout(t testingT) {
 }
 
 func testLayers(t testingT, seed int64, testTimeout bool) {
-       rand.Seed(seed)
+       t.Parallel()
+
+       r := rand.New(rand.NewSource(seed))
        errorf := func(format string, a ...interface{}) {
                t.Errorf(fmt.Sprintf("seed=%d: %s", seed, format), a...)
        }
@@ -560,7 +567,7 @@ func testLayers(t testingT, seed int64, testTimeout bool) {
                ctx       = Background()
        )
        for i := 0; i < minLayers || numTimers == 0 || len(cancels) == 0 || len(vals) == 0; i++ {
-               switch rand.Intn(3) {
+               switch r.Intn(3) {
                case 0:
                        v := new(value)
                        ctx = WithValue(ctx, v, v)
@@ -587,10 +594,12 @@ func testLayers(t testingT, seed int64, testTimeout bool) {
                        }
                }
        }
-       select {
-       case <-ctx.Done():
-               errorf("ctx should not be canceled yet")
-       default:
+       if !testTimeout {
+               select {
+               case <-ctx.Done():
+                       errorf("ctx should not be canceled yet")
+               default:
+               }
        }
        if s, prefix := fmt.Sprint(ctx), "context.Background."; !strings.HasPrefix(s, prefix) {
                t.Errorf("ctx.String() = %q want prefix %q", s, prefix)
@@ -608,7 +617,7 @@ func testLayers(t testingT, seed int64, testTimeout bool) {
                }
                checkValues("after timeout")
        } else {
-               cancel := cancels[rand.Intn(len(cancels))]
+               cancel := cancels[r.Intn(len(cancels))]
                cancel()
                select {
                case <-ctx.Done():
index b91a8acef3a9588e7c786239000f3039c53dd57e..72ac5d2e49c16cffa8d7c3ec18132a03a80677d1 100644 (file)
@@ -10,6 +10,8 @@ import (
        "time"
 )
 
+const shortDuration = 1 * time.Millisecond // a reasonable duration to block in an example
+
 // This example demonstrates the use of a cancelable context to prevent a
 // goroutine leak. By the end of the example function, the goroutine started
 // by gen will return without leaking.
@@ -55,7 +57,7 @@ func ExampleWithCancel() {
 // This example passes a context with an arbitrary deadline to tell a blocking
 // function that it should abandon its work as soon as it gets to it.
 func ExampleWithDeadline() {
-       d := time.Now().Add(50 * time.Millisecond)
+       d := time.Now().Add(shortDuration)
        ctx, cancel := context.WithDeadline(context.Background(), d)
 
        // Even though ctx will be expired, it is good practice to call its
@@ -79,7 +81,7 @@ func ExampleWithDeadline() {
 func ExampleWithTimeout() {
        // Pass a context with a timeout to tell a blocking function that it
        // should abandon its work after the timeout elapses.
-       ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond)
+       ctx, cancel := context.WithTimeout(context.Background(), shortDuration)
        defer cancel()
 
        select {