recycler

package
v0.1.21 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 21, 2026 License: MIT Imports: 1 Imported by: 0

README

recycler

Generic object-recycling primitive for the SDK kernel. Stdlib-only.

import "github.com/kitsunium/sdk/internal/kernel/recycler"

// Plain recycler — no reset; the consumer resets if it needs to.
r := recycler.NewPool[*bytes.Buffer](func() *bytes.Buffer { return new(bytes.Buffer) })
b := r.Get()
// ... use b ...
b.Reset()
r.Put(b)

Pool[T] recycles any pointer-sized typed object through a sync.Pool without boxing on the call path. CappedPool[T] (see CLAUDE.md) layers a reset-on-Put + capacity-discard policy on top, used by internal/kernel/buffer (64 KiB) and internal/core/codec/scratch (256 KiB).

See ADR 0010 for the design rationale and the layering contract.

Documentation

Overview

Package recycler — CappedPool[T] layers a reset-on-Put + cap-discard policy on top of the plain Pool[T] declared in recycler.go.

Package recycler provides Pool[T any], a generic typed object pool backed by sync.Pool. Callers recycle ANY pointer-sized object (event records, attribute slices, scratch structs) without paying the boxing cost on Get/Put. Stdlib-only and domain-neutral: any byte buffer, codec stream, HTTP body encoder, or metrics line writer can reuse it.

The byte-slice pool (internal/kernel/buffer) and the codec scratch buffer pool (internal/core/codec/scratch) are built ON this primitive; Pool[T] is the shared mechanism, the capacity thresholds stay with the consumers.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type CappedPool

type CappedPool[T any] struct {
	// contains filtered or unexported fields
}

CappedPool wraps a concrete *Pool[T] and layers a cap-discard policy on top: on Put, a value whose reported capacity exceeds maxCap is orphaned (never reset, never repooled), otherwise it is reset then repooled.

Discard happens BEFORE reset on purpose: it preserves the codec scratch detach contract, where an over-cap buffer's backing bytes have been handed to the caller and must not be touched. Reset is NOT a memory wipe — a consumer recycling sensitive bytes must zero them itself before Put.

Always used behind a pointer: the embedded *Pool owns a non-copyable sync.Pool.

func NewCappedPool

func NewCappedPool[T any](newFn func() T, resetFn func(T), capOfFn func(T) int, maxCap int) *CappedPool[T]

NewCappedPool returns a CappedPool[T]. newFn builds fresh values, resetFn returns a value to its reusable state, and capOfFn reports a value's capacity for the discard decision. All three functions are required and a nil one panics. maxCap must be positive: a non-positive threshold would orphan every value, silently turning the pool into a never-recycling allocator, so it panics rather than degrade.

func (*CappedPool[T]) Get

func (c *CappedPool[T]) Get() T

Get borrows a value of type T from the underlying recycler, invoking the factory on a cache miss. Callers own it until they hand it back via Put.

func (*CappedPool[T]) Put

func (c *CappedPool[T]) Put(v T)

Put returns v for reuse unless its capacity exceeds maxCap, in which case v is orphaned (discard-before-reset: never reset, never repooled). Otherwise v is reset then repooled. Callers MUST NOT touch v after Put returns.

type Pool

type Pool[T any] struct {
	// contains filtered or unexported fields
}

Pool is a concrete generic object pool over sync.Pool. It performs NO reset — consumers that need cleanup either reset before Put (logger Builder.Send, async data[:0]) or use CappedPool. Safe for concurrent use. Always used behind a pointer: sync.Pool must not be copied.

func NewPool

func NewPool[T any](newFn func() T) *Pool[T]

NewPool returns a Pool[T] whose factory fires on every cache miss. A nil factory is a programmer error and panics at construction rather than deferring the panic to sync.Pool.Get on the first cache miss, far from the offending call site.

func (*Pool[T]) Get

func (r *Pool[T]) Get() T

Get borrows a value of type T, invoking the factory on a cache miss. The returned value MAY be a previously Put value or a freshly built one. The comma-ok assertion is fail-loud: the pool only ever holds T (both New and Put are typed), so a wrong-typed entry is an internal invariant break that panics rather than silently masking it (mirrors core/codec/scratch.poolGet).

func (*Pool[T]) Put

func (r *Pool[T]) Put(v T)

Put returns v to the pool for future reuse. Callers MUST NOT touch v after Put returns; the pool may hand it to another goroutine immediately.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL