2020-09-21 00:33:13 +08:00
|
|
|
package pool
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"runtime"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
2022-03-16 12:10:13 +08:00
|
|
|
type Factory = func(context.Context) (any, error)
|
2020-09-21 00:33:13 +08:00
|
|
|
|
|
|
|
type entry struct {
|
2022-03-16 12:10:13 +08:00
|
|
|
elm any
|
2020-09-21 00:33:13 +08:00
|
|
|
time time.Time
|
|
|
|
}
|
|
|
|
|
|
|
|
type Option func(*pool)
|
|
|
|
|
|
|
|
// WithEvict set the evict callback
|
2022-03-16 12:10:13 +08:00
|
|
|
func WithEvict(cb func(any)) Option {
|
2020-09-21 00:33:13 +08:00
|
|
|
return func(p *pool) {
|
|
|
|
p.evict = cb
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithAge defined element max age (millisecond)
|
|
|
|
func WithAge(maxAge int64) Option {
|
|
|
|
return func(p *pool) {
|
|
|
|
p.maxAge = maxAge
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// WithSize defined max size of Pool
|
|
|
|
func WithSize(maxSize int) Option {
|
|
|
|
return func(p *pool) {
|
2022-03-16 12:10:13 +08:00
|
|
|
p.ch = make(chan any, maxSize)
|
2020-09-21 00:33:13 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Pool is for GC, see New for detail
|
|
|
|
type Pool struct {
|
|
|
|
*pool
|
|
|
|
}
|
|
|
|
|
|
|
|
type pool struct {
|
2022-03-16 12:10:13 +08:00
|
|
|
ch chan any
|
2020-09-21 00:33:13 +08:00
|
|
|
factory Factory
|
2022-03-16 12:10:13 +08:00
|
|
|
evict func(any)
|
2020-09-21 00:33:13 +08:00
|
|
|
maxAge int64
|
|
|
|
}
|
|
|
|
|
2022-03-16 12:10:13 +08:00
|
|
|
func (p *pool) GetContext(ctx context.Context) (any, error) {
|
2020-09-21 00:33:13 +08:00
|
|
|
now := time.Now()
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
case item := <-p.ch:
|
|
|
|
elm := item.(*entry)
|
|
|
|
if p.maxAge != 0 && now.Sub(item.(*entry).time).Milliseconds() > p.maxAge {
|
|
|
|
if p.evict != nil {
|
|
|
|
p.evict(elm.elm)
|
|
|
|
}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
return elm.elm, nil
|
|
|
|
default:
|
|
|
|
return p.factory(ctx)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-03-16 12:10:13 +08:00
|
|
|
func (p *pool) Get() (any, error) {
|
2020-09-21 00:33:13 +08:00
|
|
|
return p.GetContext(context.Background())
|
|
|
|
}
|
|
|
|
|
2022-03-16 12:10:13 +08:00
|
|
|
func (p *pool) Put(item any) {
|
2020-09-21 00:33:13 +08:00
|
|
|
e := &entry{
|
|
|
|
elm: item,
|
|
|
|
time: time.Now(),
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case p.ch <- e:
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
// pool is full
|
|
|
|
if p.evict != nil {
|
|
|
|
p.evict(item)
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func recycle(p *Pool) {
|
|
|
|
for item := range p.pool.ch {
|
|
|
|
if p.pool.evict != nil {
|
|
|
|
p.pool.evict(item.(*entry).elm)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func New(factory Factory, options ...Option) *Pool {
|
|
|
|
p := &pool{
|
2022-03-16 12:10:13 +08:00
|
|
|
ch: make(chan any, 10),
|
2020-09-21 00:33:13 +08:00
|
|
|
factory: factory,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, option := range options {
|
|
|
|
option(p)
|
|
|
|
}
|
|
|
|
|
|
|
|
P := &Pool{p}
|
|
|
|
runtime.SetFinalizer(P, recycle)
|
|
|
|
return P
|
|
|
|
}
|