@@ -54,16 +54,39 @@ func CheckRoutines(t *testing.T) func() {
5454}
5555
5656// CheckRoutinesStrict is used to check for leaked go-routines.
57- // It differs from CheckRoutines in that it won't wait at all
57+ // It differs from CheckRoutines in that it has very little tolerance
5858// for lingering goroutines. This is helpful for tests that need
5959// to ensure clean closure of resources.
60+ // Checking the state of goroutines exactly is tricky. As users writing
61+ // goroutines, we tend to clean up gracefully using some synchronization
62+ // pattern. When used correctly, we won't leak goroutines, but we cannot
63+ // gurantee *when* the goroutines will end. This is the nature of waiting
64+ // on the runtime's goexit1 being called which is the final subroutine
65+ // called, which is after any user written code. This small, but possible
66+ // chance to have a thread (not goroutine) be preempted before this is
67+ // called, can have our goroutine stack be not quite correct yet. The
68+ // best we can do is sleep a little bit and try to encourage the runtime
69+ // to run that goroutine (G) on the machine (M) it belongs to.
6070func CheckRoutinesStrict (tb testing.TB ) func () {
6171 tryCheckRoutinesLoop (tb , "Unexpected routines on test startup" )
6272 return func () {
73+ runtime .Gosched ()
74+ runtime .GC ()
6375 routines := getRoutines ()
6476 if len (routines ) == 0 {
6577 return
6678 }
79+ // arbitrarily short choice to allow the runtime to cleanup any
80+ // goroutines that really aren't doing anything but haven't yet
81+ // completed.
82+ time .Sleep (time .Millisecond )
83+ runtime .Gosched ()
84+ runtime .GC ()
85+ routines = getRoutines ()
86+ if len (routines ) == 0 {
87+ return
88+ }
89+
6790 tb .Fatalf ("%s: \n %s" , "Unexpected routines on test end" , strings .Join (routines , "\n \n " )) // nolint
6891 }
6992}
0 commit comments