Documentation
¶
Overview ¶
Package pools provide utilities to recycle allocated objects.
This package provides:
- a generic Pool type that wraps sync.Pool, - a PoolRedeemable variant that hands out a cached redeem closure, - a PoolSlice for recycling slices without juggling pointers.
Debug build ¶
Building with the "poolsdebug" tag (go test -tags poolsdebug ./...) turns on instrumentation that tracks every borrow and redeem and panics on misuse:
- double redeem (including the A -> B -> A case for the redeemable pools), - redeem of a foreign object, - borrow of an object still checked out
It reports the offending call sites.
AssertNoLeaks then reports any object borrowed but never redeemed.
The instrumentation is a no-op with zero overhead when the tag is absent.
Index ¶
- Constants
- func AssertNoLeaks(TB) bool
- func ResetTracking()
- type Pool
- type PoolRedeemable
- type PoolSlice
- type PoolSliceOption
- type Resettable
- type Slice
- func (s *Slice[T]) Append(elems ...T) []T
- func (s *Slice[T]) Cap() int
- func (s *Slice[T]) Clip()
- func (s *Slice[T]) Concat(slice []T) []T
- func (s *Slice[T]) Grow(size int) []T
- func (s *Slice[T]) IndexedElems() iter.Seq2[int, T]
- func (s *Slice[T]) Len() int
- func (s *Slice[T]) Reset()
- func (s *Slice[T]) Slice() []T
- type TB
Constants ¶
const DebugBuild = debugBuild
DebugBuild reports whether the pool instrumentation is compiled in (the poolsdebug build tag).
It lets a test that must run in both modes skip the parts that are invalid under instrumentation — e.g. an allocation-count assertion, since the instrumented build allocates a per-borrow tracker.
Variables ¶
This section is empty.
Functions ¶
func AssertNoLeaks ¶
AssertNoLeaks reports whether every borrowed object has been redeemed across all pools.
It is only meaningful in the instrumented build (-tags poolsdebug).
In a release build it is a no-op that always reports true, so the same test can run in both modes.
func ResetTracking ¶
func ResetTracking()
ResetTracking clears all recorded borrow/redeem tracking.
This is a no-op in a release build.
Types ¶
type Pool ¶
type Pool[T any] struct { // contains filtered or unexported fields }
Pool wraps a sync.Pool to make it available for any type.
T must be the value type of the pooled object (e.g. Pool[bytes.Buffer]): Pool.Borrow returns a *T. Using a pointer type as T (e.g. Pool[*bytes.Buffer]) would yield a **T and is almost certainly a mistake.
func New ¶
New builds a new Pool to recycle allocations of type T explicitly using Pool.Redeem and the allocated pointer.
Freshly allocated instances of type T are set to their zero value; like recycled instances they are reset (if Resettable) when borrowed, so Pool.Borrow always yields a clean object.
func (*Pool[T]) Borrow ¶
func (p *Pool[T]) Borrow() *T
Borrow an instance from the pool.
If the type implements Resettable, the returned instance is reset before being handed out, so it is always clean.
func (*Pool[T]) Redeem ¶
func (p *Pool[T]) Redeem(ptr *T)
Redeem a borrowed instance to the pool.
A nil pointer is ignored (it would otherwise corrupt the pool: a typed-nil boxed into an interface is not the nil interface that sync.Pool.Put skips).
The instance is reset (if it implements Resettable) before being returned to the pool. After calling Redeem, the caller must drop its reference to ptr: continuing to use it is a use-after-redeem bug.
Unlike PoolRedeemable, this plain pool holds no per-object state, so it cannot detect a double-redeem of the same pointer (which corrupts the pool).
Prefer PoolRedeemable when you want that guard, or the debug build for full tracking.
type PoolRedeemable ¶
type PoolRedeemable[T any] struct { // contains filtered or unexported fields }
PoolRedeemable wraps a sync.Pool to make it available for any type.
It differs from Pool in the way objects are redeemed to the pool: borrowing also yields a cached redeem closure, so no closure is allocated at redeem time.
func NewRedeemable ¶
func NewRedeemable[T any]() *PoolRedeemable[T]
NewRedeemable builds a new redeemable Pool to recycle allocations of type T, and use the inner redeemer to relinquish objects to the pool.
func (*PoolRedeemable[T]) BorrowWithRedeem ¶
func (p *PoolRedeemable[T]) BorrowWithRedeem() (*T, func())
BorrowWithRedeem borrows an instance from the pool and provides the corresponding redeem function.
This is useful for instance to use with defer.
The instance is reset (if it implements Resettable) both when borrowed and when the returned redeem closure is called. After calling the redeem closure, the caller must drop its reference to the returned instance.
Calling the redeem closure more than once panics (see [redeemable.state]): a borrowed instance must be redeemed exactly once.
type PoolSlice ¶
type PoolSlice[T any] struct { // contains filtered or unexported fields }
PoolSlice is a pool of [Slice[T]].
PoolSlice.BorrowWithRedeem will return an empty inner slice by default. This default may be altered using WithMinimumCapacity.
Use PoolSlice.BorrowWithSizeAndRedeem or Slice.Grow to grow the capacity of the inner slice.
func NewPoolSlice ¶
func NewPoolSlice[T any](opts ...PoolSliceOption) *PoolSlice[T]
NewPoolSlice builds a pool to recycle slices of type []T.
func (*PoolSlice[T]) BorrowWithRedeem ¶
BorrowWithRedeem returns the slice wrapper and the redeem closure to relinquish the allocated wrapper.
The wrapper is reset (elements zeroed, length restored) both on borrow and when the redeem closure is called. Calling the redeem closure more than once panics.
func (*PoolSlice[T]) BorrowWithSizeAndRedeem ¶
BorrowWithSizeAndRedeem borrows a slice []T from the pool and ensures that its capacity is at least the provided size.
type PoolSliceOption ¶
type PoolSliceOption func(*poolSliceOptions)
PoolSliceOption alters the default settings to allocate new pooled slices
func WithLength ¶
func WithLength(size int) PoolSliceOption
WithLength ensures that the borrowed slices have a fixed given initial length.
By default, the borrowed slices are reset to length 0.
func WithMaxCapacity ¶
func WithMaxCapacity(size int) PoolSliceOption
WithMaxCapacity bounds the capacity of recycled slices.
When a borrowed slice has grown past size at redeem time, its (oversized) backing array is discarded and replaced with a fresh one sized to the minimum capacity, instead of being recycled.
This stops the pool from accumulating large backing arrays after an occasional large request, keeping the steady-state memory bounded.
The trade-off: a workload that genuinely needs slices larger than size will reallocate on every cycle. Set size from the high-water mark you actually expect, not below it. A size of 0 (the default) means no cap: grown slices are recycled as-is.
func WithMinimumCapacity ¶
func WithMinimumCapacity(size int) PoolSliceOption
type Resettable ¶
type Resettable interface {
Reset()
}
Resettable is an interface for types that want to recycle a clean instance from a Pool.
When T (or rather *T) implements Resettable, the pool calls Reset on an instance both when it is redeemed and when it is borrowed:
- on redeem, so that no references held by the instance are retained while it sits idle in the pool (which would pin a reference graph alive across a GC cycle);
- on borrow, so that the next borrower receives a clean object regardless of how the instance reached the pool.
Reset must be safe to call more than once on the same instance (it runs at least twice per cycle).
type Slice ¶
type Slice[T any] struct { // contains filtered or unexported fields }
Slice is a struct that wraps a slice []T.
This is useful to borrow and redeem slices from a pool, without having to constantly manipulate pointers to the slice.
The wrapper holds the authoritative slice header.
Its mutating methods (Slice.Append, Slice.Concat, Slice.Grow) return the current backing slice for convenience, so it reads as an idiomatic []T.
But the returned slice is only a snapshot of the wrapper's state at that moment: if you keep it and grow it yourself with the builtin append and it reallocates, the new backing array lives only in your local copy and is NOT tracked by the wrapper — it will not be recycled when the wrapper is redeemed (and a later borrower would get the old, smaller array).
Rule of thumb: it is fine to read or pass the returned []T to a consumer; but if you plan to grow the slice, keep calling the wrapper's methods so the growth is tracked and recycled.
func (*Slice[T]) Append ¶
func (s *Slice[T]) Append(elems ...T) []T
Append elements to the inner slice and return the current backing slice.
This should be preferred to the append builtin if you plan that the slice will grow and you want the newly allocated space to be tracked and recycled. See Slice for the caveat about growing the returned slice yourself.
func (*Slice[T]) Clip ¶
func (s *Slice[T]) Clip()
Clip removes unused capacity from the inner slice.
func (*Slice[T]) Concat ¶
func (s *Slice[T]) Concat(slice []T) []T
Concat another slice to the inner slice and return the current backing slice.
Unlike slices.Concat, this reuses the inner slice's capacity instead of always allocating a fresh backing array. See Slice for the caveat about growing the returned slice yourself.
func (*Slice[T]) Grow ¶
Grow the inner slice so it can accommodate at least size more elements without reallocating, and return the current backing slice.
Growth is tracked by the wrapper, so the enlarged backing array is recycled on redeem. See Slice for the caveat about growing the returned slice yourself.
func (*Slice[T]) IndexedElems ¶
IndexedElems iterates over the inner slice.
func (*Slice[T]) Reset ¶
func (s *Slice[T]) Reset()
Reset the inner slice to its configured initial length, keeping allocated capacity.
All elements are zeroed, so the pool never retains stale element references (which would keep a referenced graph alive for slices of pointers) and so a WithLength slice is handed out clean rather than carrying data from a previous borrower.
func (*Slice[T]) Slice ¶
func (s *Slice[T]) Slice() []T
Slice returns the inner slice.
Treat the result as a read-only view (for ranging or passing to a consumer), valid until the next mutation or redeem. To grow or append, use the wrapper methods so the new backing array is tracked and recycled (see Slice).
type TB ¶
TB is the subset of testing.TB used by AssertNoLeaks.
It is satisfied by *testing.T and *testing.B.
A local interface is used (rather than importing "testing") so that the release build does not pull the testing package — and its flags — into production binaries.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package shared provides ready-made, process-wide pools for objects that are commonly recycled across potentially many packages: byte slices, bytes.Buffer and bytes.Reader.
|
Package shared provides ready-made, process-wide pools for objects that are commonly recycled across potentially many packages: byte slices, bytes.Buffer and bytes.Reader. |