Black Lives Matter. Support the Equal Justice Initiative.

Source file src/internal/poll/splice_linux_test.go

Documentation: internal/poll

     1  // Copyright 2021 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package poll_test
     6  
     7  import (
     8  	"internal/poll"
     9  	"internal/syscall/unix"
    10  	"runtime"
    11  	"syscall"
    12  	"testing"
    13  	"time"
    14  )
    15  
    16  // checkPipes returns true if all pipes are closed properly, false otherwise.
    17  func checkPipes(fds []int) bool {
    18  	for _, fd := range fds {
    19  		// Check if each pipe fd has been closed.
    20  		_, _, errno := syscall.Syscall(unix.FcntlSyscall, uintptr(fd), syscall.F_GETPIPE_SZ, 0)
    21  		if errno == 0 {
    22  			return false
    23  		}
    24  	}
    25  	return true
    26  }
    27  
    28  func TestSplicePipePool(t *testing.T) {
    29  	const N = 64
    30  	var (
    31  		p   *poll.SplicePipe
    32  		ps  []*poll.SplicePipe
    33  		fds []int
    34  		err error
    35  	)
    36  	for i := 0; i < N; i++ {
    37  		p, _, err = poll.GetPipe()
    38  		if err != nil {
    39  			t.Skip("failed to create pipe, skip this test")
    40  		}
    41  		_, pwfd := poll.GetPipeFds(p)
    42  		fds = append(fds, pwfd)
    43  		ps = append(ps, p)
    44  	}
    45  	for _, p = range ps {
    46  		poll.PutPipe(p)
    47  	}
    48  	ps = nil
    49  	p = nil
    50  
    51  	// Exploit the timeout of "go test" as a timer for the subsequent verification.
    52  	timeout := 5 * time.Minute
    53  	if deadline, ok := t.Deadline(); ok {
    54  		timeout = deadline.Sub(time.Now())
    55  		timeout -= timeout / 10 // Leave 10% headroom for cleanup.
    56  	}
    57  	expiredTime := time.NewTimer(timeout)
    58  	defer expiredTime.Stop()
    59  
    60  	// Trigger garbage collection repeatedly, waiting for all pipes in sync.Pool
    61  	// to either be deallocated and closed, or to time out.
    62  	for {
    63  		runtime.GC()
    64  		time.Sleep(10 * time.Millisecond)
    65  		if checkPipes(fds) {
    66  			break
    67  		}
    68  		select {
    69  		case <-expiredTime.C:
    70  			t.Fatal("at least one pipe is still open")
    71  		default:
    72  		}
    73  	}
    74  }
    75  
    76  func BenchmarkSplicePipe(b *testing.B) {
    77  	b.Run("SplicePipeWithPool", func(b *testing.B) {
    78  		for i := 0; i < b.N; i++ {
    79  			p, _, err := poll.GetPipe()
    80  			if err != nil {
    81  				continue
    82  			}
    83  			poll.PutPipe(p)
    84  		}
    85  	})
    86  	b.Run("SplicePipeWithoutPool", func(b *testing.B) {
    87  		for i := 0; i < b.N; i++ {
    88  			p := poll.NewPipe()
    89  			if p == nil {
    90  				b.Skip("newPipe returned nil")
    91  			}
    92  			poll.DestroyPipe(p)
    93  		}
    94  	})
    95  }
    96  
    97  func BenchmarkSplicePipePoolParallel(b *testing.B) {
    98  	b.RunParallel(func(pb *testing.PB) {
    99  		for pb.Next() {
   100  			p, _, err := poll.GetPipe()
   101  			if err != nil {
   102  				continue
   103  			}
   104  			poll.PutPipe(p)
   105  		}
   106  	})
   107  }
   108  
   109  func BenchmarkSplicePipeNativeParallel(b *testing.B) {
   110  	b.RunParallel(func(pb *testing.PB) {
   111  		for pb.Next() {
   112  			p := poll.NewPipe()
   113  			if p == nil {
   114  				b.Skip("newPipe returned nil")
   115  			}
   116  			poll.DestroyPipe(p)
   117  		}
   118  	})
   119  }
   120  

View as plain text