| package pool | |
| import ( | |
| "runtime/debug" | |
| "sync" | |
| "testing" | |
| "github.com/stretchr/testify/require" | |
| ) | |
| type pooledValue[T any] struct { | |
| value T | |
| } | |
| func TestNew(t *testing.T) { | |
| // Disable GC to avoid the victim cache during the test. | |
| defer debug.SetGCPercent(debug.SetGCPercent(-1)) | |
| p := New(func() *pooledValue[string] { | |
| return &pooledValue[string]{ | |
| value: "new", | |
| } | |
| }) | |
| // Probabilistically, 75% of sync.Pool.Put calls will succeed when -race | |
| // is enabled (see ref below); attempt to make this quasi-deterministic by | |
| // brute force (i.e., put significantly more objects in the pool than we | |
| // will need for the test) in order to avoid testing without race enabled. | |
| // | |
| // ref: https://cs.opensource.google/go/go/+/refs/tags/go1.20.2:src/sync/pool.go;l=100-103 | |
| for i := 0; i < 1_000; i++ { | |
| p.Put(&pooledValue[string]{ | |
| value: t.Name(), | |
| }) | |
| } | |
| // Ensure that we always get the expected value. Note that this must only | |
| // run a fraction of the number of times that Put is called above. | |
| for i := 0; i < 10; i++ { | |
| func() { | |
| x := p.Get() | |
| defer p.Put(x) | |
| require.Equal(t, t.Name(), x.value) | |
| }() | |
| } | |
| // Depool all objects that might be in the pool to ensure that it's empty. | |
| for i := 0; i < 1_000; i++ { | |
| p.Get() | |
| } | |
| // Now that the pool is empty, it should use the value specified in the | |
| // underlying sync.Pool.New func. | |
| require.Equal(t, "new", p.Get().value) | |
| } | |
| func TestNew_Race(t *testing.T) { | |
| p := New(func() *pooledValue[int] { | |
| return &pooledValue[int]{ | |
| value: -1, | |
| } | |
| }) | |
| var wg sync.WaitGroup | |
| defer wg.Wait() | |
| // Run a number of goroutines that read and write pool object fields to | |
| // tease out races. | |
| for i := 0; i < 1_000; i++ { | |
| i := i | |
| wg.Add(1) | |
| go func() { | |
| defer wg.Done() | |
| x := p.Get() | |
| defer p.Put(x) | |
| // Must both read and write the field. | |
| if n := x.value; n >= -1 { | |
| x.value = i | |
| } | |
| }() | |
| } | |
| } | |