...

Source file src/go.uber.org/zap/internal/pool/pool_test.go

Documentation: go.uber.org/zap/internal/pool

     1  // Copyright (c) 2023 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package pool_test
    22  
    23  import (
    24  	"runtime/debug"
    25  	"sync"
    26  	"testing"
    27  
    28  	"github.com/stretchr/testify/require"
    29  	"go.uber.org/zap/internal/pool"
    30  )
    31  
    32  type pooledValue[T any] struct {
    33  	value T
    34  }
    35  
    36  func TestNew(t *testing.T) {
    37  	// Disable GC to avoid the victim cache during the test.
    38  	defer debug.SetGCPercent(debug.SetGCPercent(-1))
    39  
    40  	p := pool.New(func() *pooledValue[string] {
    41  		return &pooledValue[string]{
    42  			value: "new",
    43  		}
    44  	})
    45  
    46  	// Probabilistically, 75% of sync.Pool.Put calls will succeed when -race
    47  	// is enabled (see ref below); attempt to make this quasi-deterministic by
    48  	// brute force (i.e., put significantly more objects in the pool than we
    49  	// will need for the test) in order to avoid testing without race enabled.
    50  	//
    51  	// ref: https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/sync/pool.go;l=100-103
    52  	for i := 0; i < 1_000; i++ {
    53  		p.Put(&pooledValue[string]{
    54  			value: t.Name(),
    55  		})
    56  	}
    57  
    58  	// Ensure that we always get the expected value. Note that this must only
    59  	// run a fraction of the number of times that Put is called above.
    60  	for i := 0; i < 10; i++ {
    61  		func() {
    62  			x := p.Get()
    63  			defer p.Put(x)
    64  			require.Equal(t, t.Name(), x.value)
    65  		}()
    66  	}
    67  
    68  	// Depool all objects that might be in the pool to ensure that it's empty.
    69  	for i := 0; i < 1_000; i++ {
    70  		p.Get()
    71  	}
    72  
    73  	// Now that the pool is empty, it should use the value specified in the
    74  	// underlying sync.Pool.New func.
    75  	require.Equal(t, "new", p.Get().value)
    76  }
    77  
    78  func TestNew_Race(t *testing.T) {
    79  	p := pool.New(func() *pooledValue[int] {
    80  		return &pooledValue[int]{
    81  			value: -1,
    82  		}
    83  	})
    84  
    85  	var wg sync.WaitGroup
    86  	defer wg.Wait()
    87  
    88  	// Run a number of goroutines that read and write pool object fields to
    89  	// tease out races.
    90  	for i := 0; i < 1_000; i++ {
    91  		i := i
    92  
    93  		wg.Add(1)
    94  		go func() {
    95  			defer wg.Done()
    96  
    97  			x := p.Get()
    98  			defer p.Put(x)
    99  
   100  			// Must both read and write the field.
   101  			if n := x.value; n >= -1 {
   102  				x.value = i
   103  			}
   104  		}()
   105  	}
   106  }
   107  

View as plain text