// Copyright 2021 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package poll_test import ( "internal/poll" "internal/syscall/unix" "runtime" "syscall" "testing" "time" ) // checkPipes returns true if all pipes are closed properly, false otherwise. func checkPipes(fds []int) bool { for _, fd := range fds { // Check if each pipe fd has been closed. _, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0) if errno == 0 { return false } } return true } func TestSplicePipePool(t *testing.T) { const N = 64 var ( p *poll.SplicePipe ps []*poll.SplicePipe fds []int err error ) for i := 0; i < N; i++ { p, _, err = poll.GetPipe() if err != nil { t.Skip("failed to create pipe, skip this test") } _, pwfd := poll.GetPipeFds(p) fds = append(fds, pwfd) ps = append(ps, p) } for _, p = range ps { poll.PutPipe(p) } ps = nil p = nil // Exploit the timeout of "go test" as a timer for the subsequent verification. timeout := 5 * time.Minute if deadline, ok := t.Deadline(); ok { 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() time.Sleep(10 * time.Millisecond) if checkPipes(fds) { break } select { case <-expiredTime.C: t.Fatal("at least one pipe is still open") default: } } } func BenchmarkSplicePipe(b *testing.B) { b.Run("SplicePipeWithPool", func(b *testing.B) { for i := 0; i < b.N; i++ { p, _, err := poll.GetPipe() if err != nil { continue } poll.PutPipe(p) } }) b.Run("SplicePipeWithoutPool", func(b *testing.B) { for i := 0; i < b.N; i++ { p := poll.NewPipe() if p == nil { b.Skip("newPipe returned nil") } poll.DestroyPipe(p) } }) } func BenchmarkSplicePipePoolParallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { p, _, err := poll.GetPipe() if err != nil { continue } poll.PutPipe(p) } }) } func BenchmarkSplicePipeNativeParallel(b *testing.B) { b.RunParallel(func(pb *testing.PB) { for pb.Next() { p := poll.NewPipe() if p == nil { b.Skip("newPipe returned nil") } poll.DestroyPipe(p) } }) }