mirror of https://github.com/golang/go.git
internal/poll: inject a hook into the runtime finalizer to count the closed pipes
Fixes #48066 Change-Id: Icd6974dfcc496c054bb096e5d70de6e135984517 Reviewed-on: https://go-review.googlesource.com/c/go/+/349774 Reviewed-by: Bryan C. Mills <bcmills@google.com> Reviewed-by: Ian Lance Taylor <iant@golang.org> Trust: Bryan C. Mills <bcmills@google.com> Run-TryBot: Bryan C. Mills <bcmills@google.com> TryBot-Result: Go Bot <gobot@golang.org>
This commit is contained in:
parent
323c6f74d3
commit
74e384f50d
|
|
@ -6,23 +6,25 @@ package poll_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"internal/poll"
|
"internal/poll"
|
||||||
"internal/syscall/unix"
|
|
||||||
"runtime"
|
"runtime"
|
||||||
"syscall"
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// checkPipes returns true if all pipes are closed properly, false otherwise.
|
var closeHook atomic.Value // func(fd int)
|
||||||
func checkPipes(fds []int) bool {
|
|
||||||
for _, fd := range fds {
|
func init() {
|
||||||
// Check if each pipe fd has been closed.
|
closeFunc := poll.CloseFunc
|
||||||
_, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0)
|
poll.CloseFunc = func(fd int) (err error) {
|
||||||
if errno == 0 {
|
if v := closeHook.Load(); v != nil {
|
||||||
return false
|
if hook := v.(func(int)); hook != nil {
|
||||||
|
hook(fd)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
return closeFunc(fd)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestSplicePipePool(t *testing.T) {
|
func TestSplicePipePool(t *testing.T) {
|
||||||
|
|
@ -30,16 +32,22 @@ func TestSplicePipePool(t *testing.T) {
|
||||||
var (
|
var (
|
||||||
p *poll.SplicePipe
|
p *poll.SplicePipe
|
||||||
ps []*poll.SplicePipe
|
ps []*poll.SplicePipe
|
||||||
fds []int
|
allFDs []int
|
||||||
|
pendingFDs sync.Map // fd → struct{}{}
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
closeHook.Store(func(fd int) { pendingFDs.Delete(fd) })
|
||||||
|
t.Cleanup(func() { closeHook.Store((func(int))(nil)) })
|
||||||
|
|
||||||
for i := 0; i < N; i++ {
|
for i := 0; i < N; i++ {
|
||||||
p, _, err = poll.GetPipe()
|
p, _, err = poll.GetPipe()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Skip("failed to create pipe, skip this test")
|
t.Skipf("failed to create pipe due to error(%v), skip this test", err)
|
||||||
}
|
}
|
||||||
_, pwfd := poll.GetPipeFds(p)
|
_, pwfd := poll.GetPipeFds(p)
|
||||||
fds = append(fds, pwfd)
|
allFDs = append(allFDs, pwfd)
|
||||||
|
pendingFDs.Store(pwfd, struct{}{})
|
||||||
ps = append(ps, p)
|
ps = append(ps, p)
|
||||||
}
|
}
|
||||||
for _, p = range ps {
|
for _, p = range ps {
|
||||||
|
|
@ -62,19 +70,21 @@ func TestSplicePipePool(t *testing.T) {
|
||||||
for {
|
for {
|
||||||
runtime.GC()
|
runtime.GC()
|
||||||
time.Sleep(10 * time.Millisecond)
|
time.Sleep(10 * time.Millisecond)
|
||||||
if checkPipes(fds) {
|
|
||||||
|
// Detect whether all pipes are closed properly.
|
||||||
|
var leakedFDs []int
|
||||||
|
pendingFDs.Range(func(k, v interface{}) bool {
|
||||||
|
leakedFDs = append(leakedFDs, k.(int))
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
if len(leakedFDs) == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
select {
|
select {
|
||||||
case <-expiredTime.C:
|
case <-expiredTime.C:
|
||||||
t.Logf("descriptors to check: %v", fds)
|
t.Logf("all descriptors: %v", allFDs)
|
||||||
for _, fd := range fds {
|
t.Fatalf("leaked descriptors: %v", leakedFDs)
|
||||||
_, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0)
|
|
||||||
if errno == 0 {
|
|
||||||
t.Errorf("descriptor %d still open", fd)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
t.Fatal("at least one pipe is still open")
|
|
||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue