Skip to content

Commit 1c59066

Browse files
author
Bryan C. Mills
committed
testing: allow parallel-subtest goroutines to exit when the subtest is complete
Fixes #45127 Updates #38768 Change-Id: I7f41901d5bcc07741ac9f5f2a24d2b07ef633cb1 Reviewed-on: https://go-review.googlesource.com/c/go/+/303330 Trust: Bryan C. Mills <[email protected]> Run-TryBot: Bryan C. Mills <[email protected]> TryBot-Result: Go Bot <[email protected]> Reviewed-by: Ian Lance Taylor <[email protected]>
1 parent 4829031 commit 1c59066

File tree

2 files changed

+59
-6
lines changed

2 files changed

+59
-6
lines changed
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Regression test for https://golang.org/issue/45127:
2+
# Goroutines for completed parallel subtests should exit immediately,
3+
# not block until earlier subtests have finished.
4+
5+
[short] skip
6+
7+
! go test .
8+
stdout 'panic: slow failure'
9+
! stdout '\[chan send'
10+
11+
-- go.mod --
12+
module golang.org/issue45127
13+
14+
go 1.16
15+
-- issue45127_test.go --
16+
package main
17+
18+
import (
19+
"fmt"
20+
"runtime"
21+
"runtime/debug"
22+
"sync"
23+
"testing"
24+
)
25+
26+
func TestTestingGoroutineLeak(t *testing.T) {
27+
debug.SetTraceback("all")
28+
29+
var wg sync.WaitGroup
30+
const nFast = 10
31+
32+
t.Run("slow", func(t *testing.T) {
33+
t.Parallel()
34+
wg.Wait()
35+
for i := 0; i < nFast; i++ {
36+
// If the subtest goroutines are going to park on the channel
37+
// send, allow them to park now. If they're not going to park,
38+
// make sure they have had a chance to run to completion so
39+
// that they aren't spuriously parked when we panic.
40+
runtime.Gosched()
41+
}
42+
panic("slow failure")
43+
})
44+
45+
wg.Add(nFast)
46+
for i := 0; i < nFast; i++ {
47+
t.Run(fmt.Sprintf("leaky%d", i), func(t *testing.T) {
48+
t.Parallel()
49+
wg.Done()
50+
})
51+
}
52+
}

src/testing/testing.go

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1258,7 +1258,7 @@ func (t *T) Run(name string, f func(t *T)) bool {
12581258
t = &T{
12591259
common: common{
12601260
barrier: make(chan bool),
1261-
signal: make(chan bool),
1261+
signal: make(chan bool, 1),
12621262
name: testName,
12631263
parent: &t.common,
12641264
level: t.level + 1,
@@ -1539,7 +1539,7 @@ func runTests(matchString func(pat, str string) (bool, error), tests []InternalT
15391539
ctx.deadline = deadline
15401540
t := &T{
15411541
common: common{
1542-
signal: make(chan bool),
1542+
signal: make(chan bool, 1),
15431543
barrier: make(chan bool),
15441544
w: os.Stdout,
15451545
},
@@ -1552,11 +1552,12 @@ func runTests(matchString func(pat, str string) (bool, error), tests []InternalT
15521552
for _, test := range tests {
15531553
t.Run(test.Name, test.F)
15541554
}
1555-
// Run catching the signal rather than the tRunner as a separate
1556-
// goroutine to avoid adding a goroutine during the sequential
1557-
// phase as this pollutes the stacktrace output when aborting.
1558-
go func() { <-t.signal }()
15591555
})
1556+
select {
1557+
case <-t.signal:
1558+
default:
1559+
panic("internal error: tRunner exited without sending on t.signal")
1560+
}
15601561
ok = ok && !t.Failed()
15611562
ran = ran || t.ran
15621563
}

0 commit comments

Comments
 (0)