114 lines
2.0 KiB
Go
114 lines
2.0 KiB
Go
package genpool
|
|
|
|
import "errors"
|
|
|
|
var (
|
|
ErrPoolClosed = errors.New("pool already closed")
|
|
)
|
|
|
|
type getResp[Item interface{}] struct {
|
|
Item *Item
|
|
err error
|
|
}
|
|
|
|
type poolReq[Item interface{}] struct {
|
|
get chan<- getResp[Item]
|
|
ret *Item
|
|
}
|
|
|
|
type PoolManager[Item interface{}] interface {
|
|
Destroy(*Item)
|
|
Create() (*Item, error)
|
|
Validate(*Item) bool
|
|
}
|
|
|
|
type poolBackend[Item interface{}] struct {
|
|
manager PoolManager[Item]
|
|
pool []*Item
|
|
}
|
|
|
|
func (b *poolBackend[Item]) run(ch <-chan poolReq[Item]) {
|
|
defer func() {
|
|
for _, item := range b.pool {
|
|
b.manager.Destroy(item)
|
|
}
|
|
}()
|
|
for req := range ch {
|
|
if req.get != nil {
|
|
// fetch an item
|
|
for {
|
|
if len(b.pool) > 0 {
|
|
item := b.pool[len(b.pool)-1]
|
|
b.pool = b.pool[:len(b.pool)-1]
|
|
if b.manager.Validate(item) {
|
|
req.get <- getResp[Item]{
|
|
Item: item,
|
|
err: nil,
|
|
}
|
|
break
|
|
} else {
|
|
b.manager.Destroy(item)
|
|
}
|
|
} else {
|
|
// last item
|
|
item, err := b.manager.Create()
|
|
req.get <- getResp[Item]{
|
|
Item: item,
|
|
err: err,
|
|
}
|
|
break
|
|
}
|
|
}
|
|
}
|
|
if req.ret != nil {
|
|
// return an item to the pool
|
|
if cap(b.pool) > len(b.pool) {
|
|
b.pool = append(b.pool, req.ret)
|
|
} else {
|
|
b.manager.Destroy(req.ret)
|
|
}
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
type Pool[Item interface{}] struct {
|
|
req chan<- poolReq[Item]
|
|
}
|
|
|
|
func NewPool[Item interface{}](manager PoolManager[Item], capacity int) Pool[Item] {
|
|
cmdChan := make(chan poolReq[Item])
|
|
backend := poolBackend[Item]{
|
|
manager: manager,
|
|
pool: make([]*Item, 0, capacity),
|
|
}
|
|
go backend.run(cmdChan)
|
|
return Pool[Item]{
|
|
req: cmdChan,
|
|
}
|
|
}
|
|
|
|
func (p *Pool[Item]) Get() (*Item, error) {
|
|
retCh := make(chan getResp[Item])
|
|
p.req <- poolReq[Item]{
|
|
get: retCh,
|
|
}
|
|
item, ok := <-retCh
|
|
if ok {
|
|
return item.Item, item.err
|
|
} else {
|
|
return nil, ErrPoolClosed
|
|
}
|
|
}
|
|
|
|
func (p *Pool[Item]) Put(item *Item) {
|
|
p.req <- poolReq[Item]{
|
|
ret: item,
|
|
}
|
|
}
|
|
|
|
func (p *Pool[Item]) Close() {
|
|
close(p.req)
|
|
p.req = nil
|
|
}
|