Generic, exact-capacity object pool for Go 1.26.5+.
TryGet and TryPut execute no caller callbacks, allocate no memory, start no goroutines, and use bounded atomic probes. Callers explicitly own creation, reset, rejection, and cleanup policy.
go get github.com/colduction/boundedpool-go@latestpackage main
import (
"bytes"
"errors"
"github.com/colduction/boundedpool-go"
)
func main() {
pool, err := boundedpool.New[*bytes.Buffer](1024)
if err != nil {
panic(err)
}
buf, err := pool.TryGet()
switch {
case errors.Is(err, boundedpool.ErrUnavailable):
buf = new(bytes.Buffer)
case errors.Is(err, boundedpool.ErrClosed):
return
}
buf.WriteString("hello")
buf.Reset()
switch err := pool.TryPut(buf); {
case err == nil:
// Pool owns buf.
case errors.Is(err, boundedpool.ErrUnavailable), errors.Is(err, boundedpool.ErrClosed):
// Caller still owns buf and may dispose or retry it.
}
items, first := pool.Close()
if first {
for _, item := range items {
// Dispose retained items when T owns external resources.
_ = item
}
}
}| Call | Result |
|---|---|
TryGet |
A nil error transfers one idle item to caller. |
TryPut |
A nil error transfers item to pool. Rejection keeps caller ownership. |
Close |
Seals storage and returns every retained item to winning caller. |
Len |
Approximate idle count; zero once Closed reports true. |
Cap |
Exact maximum retained item count. |
Closed |
Whether new operations are rejected. |
ErrUnavailable means no candidate won during one bounded scan. In a quiescent pool this means empty for TryGet or full for TryPut; concurrent ownership transfers can make that observation stale immediately. Retry, create, discard, or shed according to caller policy.
Close performs no callbacks. It marks every slot closed, waits only for an ownership transfer already holding a slot, compacts retained items in the existing backing array, then returns them without allocating. Concurrent losing Close calls wait for drain completion and return nil, false. Checked-out and rejected items remain caller-owned.
An operation overlapping Close may return a transfer that linearized first or ErrClosed; it cannot access returned storage after Close finishes.
| Option | Default | Purpose |
|---|---|---|
WithShardFactor |
2 |
Requests factor × GOMAXPROCS shards. |
WithNumShards |
— | Requests explicit shard count; overrides factor. |
Shard count is normalized to a power of two, capped by capacity, and raised when needed to keep each shard at most 64 slots. Every call probes at most Cap slots. This preserves deterministic capacity and ownership; stable empty/full scans are O(Cap) but never wait or allocate.
Never use an item after successful TryPut, put one item twice, or copy a pool after first use. Prefer pointers for values containing mutexes, atomics, or other non-copyable state.
sync.Pool may discard cached values at any GC cycle. boundedpool-go retains idle items up to exact configured capacity until Close, then returns them to caller control.
GNU Lesser General Public License v2.1. See LICENSE.