Skip to content

Commit

Permalink
adt/pool: buffer pool constructor testings and semantics
Browse files Browse the repository at this point in the history
  • Loading branch information
tychoish committed Aug 24, 2023
1 parent 4bb19a7 commit 08e7254
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 1 deletion.
4 changes: 3 additions & 1 deletion adt/pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ func (p *Pool[T]) doInit() {
func (p *Pool[T]) FinalizeSetup() { p.init(); p.locked.Store(true) }

// SetCleanupHook sets a function to be called on every object
// renetering the pool. By default, the cleanup function is a noop.
// renetering the pool. By default, the cleanup function is a noop,
// and if the input function is nil, it is not set.
func (p *Pool[T]) SetCleanupHook(in func(T) T) {
p.init()
fun.Invariant.IsFalse(p.locked.Load(), "SetCleaupHook", "after FinalizeSetup", ers.ErrImmutabilityViolation)
Expand All @@ -76,6 +77,7 @@ func (p *Pool[T]) Get() T { p.init(); return p.pool.Get().(T) }
// cleanuphook or returning it to the pool.
func (p *Pool[T]) Put(in T) {
p.init()

if any(in) != nil {
p.pool.Put(p.hook.Get()(in))
}
Expand Down
112 changes: 112 additions & 0 deletions adt/pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package adt

import (
"context"
"math/rand"
"runtime"
"sync"
"sync/atomic"
Expand All @@ -11,6 +12,7 @@ import (
"github.com/tychoish/fun"
"github.com/tychoish/fun/assert"
"github.com/tychoish/fun/assert/check"
"github.com/tychoish/fun/ft"
"github.com/tychoish/fun/testt"
)

Expand Down Expand Up @@ -165,4 +167,114 @@ func TestPool(t *testing.T) {
<-ctx.Done()
assert.True(t, seen.Load())
})
t.Run("Finalize", func(t *testing.T) {
p := &Pool[int]{}

ft.DoTimes(100, func() { p.SetConstructor(rand.Int) })
p.SetConstructor(nil)
assert.NotNil(t, p.constructor.Load())
p.SetCleanupHook(nil)
assert.NotNil(t, p.hook.Load())

ft.DoTimes(100, p.FinalizeSetup)
check.Panic(t, func() { p.SetConstructor(rand.Int) })
check.Panic(t, func() { p.SetCleanupHook(func(int) int { return 0 }) })
check.Panic(t, func() { p.SetConstructor(nil) })
check.Panic(t, func() { p.SetCleanupHook(nil) })
})
t.Run("MakeBytesBufferPool", func(t *testing.T) {
bp := MakeBytesBufferPool(1024)
buf := bp.Get()
check.Equal(t, buf.Cap(), 1024)

var passed bool
var count int64
for start := time.Now(); time.Since(start) < 2*time.Second; {
count++
ft.DoTimes(2048, func() { buf.WriteByte('!') })
bp.Put(buf)
buf = bp.Get()
check.Equal(t, buf.Len(), 0)
if buf.Cap() >= 2048 {
passed = true
break
}
}
testt.Log(t, "attempts:", count)
check.True(t, passed)

})
t.Run("BytesBuffer", func(t *testing.T) {
t.Run("Resizes", func(t *testing.T) {
for name, bufpool := range map[string]*Pool[[]byte]{
"Small": MakeBufferPool(0, 32),
"Default": DefaultBufferPool(),
} {
t.Run(name, func(t *testing.T) {
buf := bufpool.Get()
check.Equal(t, cap(buf), 0)
check.Equal(t, len(buf), 0)
bufpool.Put(buf)

var passed bool
var count int64
for start := time.Now(); time.Since(start) < 2*time.Second; {
count++
buf = bufpool.Get()
assert.True(t, cap(buf) <= 32)
assert.Zero(t, len(buf))

if cap(buf) >= 16 {
passed = true
break
}

for len(buf) < 32 {
buf = append(buf, '~')
}
bufpool.Put(buf)
}

testt.Log(t, "attempts:", count)
check.True(t, passed)
})
}

})
t.Run("OversizeSafely", func(t *testing.T) {
// hard to introspect if they're actually in
// the pool. Not panicing is good.
check.NotPanic(t, func() {
p := MakeBufferPool(0, 4)
buf := p.Get()
for len(buf) < 32 {
buf = append(buf, '~')
}
p.Put(buf)
})
})
t.Run("PanicsForZeroMax", func(t *testing.T) {
check.Panic(t, func() { MakeBufferPool(0, 0) })
check.Panic(t, func() { MakeBufferPool(-100, -2) })
})
t.Run("BoundsAreFlooredAtZero", func(t *testing.T) {
check.NotPanic(t, func() { MakeBufferPool(-100, 100) })
check.NotPanic(t, func() { MakeBufferPool(100, -100) })
})
t.Run("FlipsIfNeeded", func(t *testing.T) {
for name, bufpool := range map[string]*Pool[[]byte]{
"MinMax": MakeBufferPool(32, 64),
"MaxMin": MakeBufferPool(64, 32),
} {
t.Run(name, func(t *testing.T) {
buf := bufpool.Get()
check.Equal(t, cap(buf), 32)
check.Equal(t, len(buf), 0)
})
}

})

})

}

0 comments on commit 08e7254

Please sign in to comment.