internal/poll: fix the intermittent build failures with pipe pool

Correlative CL 308089

Fixes #45059

Change-Id: I1ff9fbf64e6620d651f287ba2a28d40f964d78a3
Reviewed-on: https://go-review.googlesource.com/c/go/+/308329
Run-TryBot: Ian Lance Taylor <iant@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Trust: Emmanuel Odeke <emmanuel@orijtech.com>
This commit is contained in:
Andy Pan 2021-04-08 11:42:53 +08:00 committed by Emmanuel Odeke
parent 52bf14e0e8
commit 6382ec1aba

View file

@ -6,6 +6,7 @@ package poll_test
import ( import (
"internal/poll" "internal/poll"
"internal/syscall/unix"
"runtime" "runtime"
"syscall" "syscall"
"testing" "testing"
@ -16,8 +17,8 @@ import (
func checkPipes(fds []int) bool { func checkPipes(fds []int) bool {
for _, fd := range fds { for _, fd := range fds {
// Check if each pipe fd has been closed. // Check if each pipe fd has been closed.
err := syscall.FcntlFlock(uintptr(fd), syscall.F_GETFD, nil) _, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0)
if err == nil { if errno == 0 {
return false return false
} }
} }
@ -37,8 +38,8 @@ func TestSplicePipePool(t *testing.T) {
if err != nil { if err != nil {
t.Skip("failed to create pipe, skip this test") t.Skip("failed to create pipe, skip this test")
} }
prfd, pwfd := poll.GetPipeFds(p) _, pwfd := poll.GetPipeFds(p)
fds = append(fds, prfd, pwfd) fds = append(fds, pwfd)
ps = append(ps, p) ps = append(ps, p)
} }
for _, p = range ps { for _, p = range ps {
@ -46,19 +47,28 @@ func TestSplicePipePool(t *testing.T) {
} }
ps = nil ps = nil
var ok bool // Exploit the timeout of "go test" as a timer for the subsequent verification.
// Trigger garbage collection to free the pipes in sync.Pool and check whether or not timeout := 5 * time.Minute
// those pipe buffers have been closed as we expected. if deadline, ok := t.Deadline(); ok {
for i := 0; i < 5; i++ { timeout = deadline.Sub(time.Now())
timeout -= timeout / 10 // Leave 10% headroom for cleanup.
}
expiredTime := time.NewTimer(timeout)
defer expiredTime.Stop()
// Trigger garbage collection repeatedly, waiting for all pipes in sync.Pool
// to either be deallocated and closed, or to time out.
for {
runtime.GC() runtime.GC()
time.Sleep(time.Duration(i*100+10) * time.Millisecond) time.Sleep(10 * time.Millisecond)
if ok = checkPipes(fds); ok { if checkPipes(fds) {
break break
} }
} select {
case <-expiredTime.C:
if !ok { t.Fatal("at least one pipe is still open")
t.Fatal("at least one pipe is still open") default:
}
} }
} }