func (pool *ObjectPool) Close(ctx context.Context) {
	if pool.IsClosed() {
		return
	}
	pool.closeLock.Lock()
	defer pool.closeLock.Unlock()
	if pool.closed {
		return
	}
	pool.startEvictor(-1)
	pool.closed = true
	pool.Clear(ctx)
	pool.idleObjects.InterruptTakeWaiters()
}
Close pool, and free any resources associated with it.
package pool
import (
	"context"
	"errors"
	"math"
	"sync"
	"time"
	"github.com/jolestar/go-commons-pool/v2/collections"
	"github.com/jolestar/go-commons-pool/v2/concurrent"
)
type baseErr struct {
	msg string
}
func (err *baseErr) Error() string {
	return err.msg
}
type IllegalStateErr struct {
	baseErr
}
func NewIllegalStateErr(msg string) *IllegalStateErr {
	return &IllegalStateErr{baseErr{msg}}
}
type NoSuchElementErr struct {
	baseErr
}
func NewNoSuchElementErr(msg string) *NoSuchElementErr {
	return &NoSuchElementErr{baseErr{msg}}
}
type ObjectPool struct {
	AbandonedConfig                  *AbandonedConfig
	Config                           *ObjectPoolConfig
	closed                           bool
	closeLock                        sync.Mutex
	evictionLock                     sync.Mutex
	idleObjects                      *collections.LinkedBlockingDeque
	allObjects                       *collections.SyncIdentityMap
	factory                          PooledObjectFactory
	createCount                      concurrent.AtomicInteger
	destroyedByEvictorCount          concurrent.AtomicInteger
	destroyedCount                   concurrent.AtomicInteger
	destroyedByBorrowValidationCount concurrent.AtomicInteger
	evictor                          *time.Ticker
	evictorStopChan                  chan struct{}
	evictorStopWG                    sync.WaitGroup
	evictionIterator                 collections.Iterator
}
func NewObjectPool(ctx context.Context, factory PooledObjectFactory, config *ObjectPoolConfig) *ObjectPool {
	return NewObjectPoolWithAbandonedConfig(ctx, factory, config, nil)
}
func NewObjectPoolWithDefaultConfig(ctx context.Context, factory PooledObjectFactory) *ObjectPool {
	return NewObjectPool(ctx, factory, NewDefaultPoolConfig())
}
func NewObjectPoolWithAbandonedConfig(ctx context.Context, factory PooledObjectFactory, config *ObjectPoolConfig, abandonedConfig *AbandonedConfig) *ObjectPool {
	pool := ObjectPool{factory: factory, Config: config,
		idleObjects:             collections.NewDeque(math.MaxInt32),
		allObjects:              collections.NewSyncMap(),
		createCount:             concurrent.AtomicInteger(0),
		destroyedByEvictorCount: concurrent.AtomicInteger(0),
		destroyedCount:          concurrent.AtomicInteger(0),
		AbandonedConfig:         abandonedConfig}
	pool.StartEvictor()
	return &pool
}
func (pool *ObjectPool) AddObject(ctx context.Context) error {
	if pool.IsClosed() {
		return NewIllegalStateErr("Pool not open")
	}
	if pool.factory == nil {
		return NewIllegalStateErr("Cannot add objects without a factory.")
	}
	p, e := pool.create(ctx)
	if e != nil {
		return e
	}
	e = pool.addIdleObject(ctx, p)
	if e != nil {
		pool.destroy(ctx, p)
		return e
	}
	return nil
}
func (pool *ObjectPool) addIdleObject(ctx context.Context, p *PooledObject) error {
	if p != nil {
		err := pool.factory.PassivateObject(ctx, p)
		if err != nil {
			return err
		}
		if pool.Config.LIFO {
			err = pool.idleObjects.AddFirst(p)
		} else {
			err = pool.idleObjects.AddLast(p)
		}
		if err != nil {
			return err
		}
	}
	return nil
}
func (pool *ObjectPool) BorrowObject(ctx context.Context) (interface{}, error) {
	return pool.borrowObject(ctx)
}
func (pool *ObjectPool) GetNumIdle() int {
	return pool.idleObjects.Size()
}
func (pool *ObjectPool) GetNumActive() int {
	return pool.allObjects.Size() - pool.idleObjects.Size()
}
func (pool *ObjectPool) GetDestroyedCount() int {
	return int(pool.destroyedCount.Get())
}
func (pool *ObjectPool) GetDestroyedByBorrowValidationCount() int {
	return int(pool.destroyedByBorrowValidationCount.Get())
}
func (pool *ObjectPool) removeAbandoned(ctx context.Context, config *AbandonedConfig) {
	var remove []*PooledObject
	objects := pool.allObjects.Values()
	for _, o := range objects {
		pooledObject := o.(*PooledObject)
		pooledObject.lock.Lock()
		if pooledObject.state == StateAllocated &&
			time.Since(pooledObject.GetLastUsedTime()) > config.RemoveAbandonedTimeout {
			pooledObject.markAbandoned()
			remove = append(remove, pooledObject)
		}
		pooledObject.lock.Unlock()
	}
	for _, pooledObject := range remove {
		pool.InvalidateObject(ctx, pooledObject.Object)
	}
}
func (pool *ObjectPool) create(ctx context.Context) (*PooledObject, error) {
	localMaxTotal := pool.Config.MaxTotal
	newCreateCount := pool.createCount.IncrementAndGet()
	if localMaxTotal > -1 && int(newCreateCount) > localMaxTotal ||
		newCreateCount >= math.MaxInt32 {
		pool.createCount.DecrementAndGet()
		return nil, nil
	}
	p, e := pool.factory.MakeObject(ctx)
	if e != nil {
		pool.createCount.DecrementAndGet()
		return nil, e
	}
	pool.allObjects.Put(p.Object, p)
	return p, nil
}
func (pool *ObjectPool) destroy(ctx context.Context, toDestroy *PooledObject) {
	pool.doDestroy(ctx, toDestroy, false)
}
func (pool *ObjectPool) doDestroy(ctx context.Context, toDestroy *PooledObject, inLock bool) {
	if inLock {
		toDestroy.invalidate()
	} else {
		toDestroy.Invalidate()
	}
	pool.idleObjects.RemoveFirstOccurrence(toDestroy)
	pool.allObjects.Remove(toDestroy.Object)
	pool.factory.DestroyObject(ctx, toDestroy)
	pool.destroyedCount.IncrementAndGet()
	pool.createCount.DecrementAndGet()
}
func (pool *ObjectPool) updateStatsBorrow(object *PooledObject, time time.Duration) {
}
func (pool *ObjectPool) updateStatsReturn(activeTime time.Duration) {
}
func (pool *ObjectPool) borrowObject(ctx context.Context) (interface{}, error) {
	if pool.IsClosed() {
		return nil, NewIllegalStateErr("Pool not open")
	}
	ac := pool.AbandonedConfig
	if ac != nil && ac.RemoveAbandonedOnBorrow &&
		(pool.GetNumIdle() < 2) &&
		(pool.GetNumActive() > pool.Config.MaxTotal-3) {
		pool.removeAbandoned(ctx, ac)
	}
	var p *PooledObject
	var e error
	blockWhenExhausted := pool.Config.BlockWhenExhausted
	var create bool
	waitTime := time.Now()
	var ok bool
	for p == nil {
		create = false
		if blockWhenExhausted {
			p, ok = pool.idleObjects.PollFirst().(*PooledObject)
			if !ok {
				p, e = pool.create(ctx)
				if e != nil {
					return nil, e
				}
				if p != nil {
					create = true
					ok = true
				}
			}
			if p == nil {
				obj, err := pool.idleObjects.PollFirstWithContext(ctx)
				if err != nil {
					return nil, err
				}
				p, ok = obj.(*PooledObject)
			}
			if !ok {
				return nil, NewNoSuchElementErr("Timeout waiting for idle object")
			}
			if !p.Allocate() {
				p = nil
			}
		} else {
			p, ok = pool.idleObjects.PollFirst().(*PooledObject)
			if !ok {
				p, e = pool.create(ctx)
				if e != nil {
					return nil, e
				}
				if p != nil {
					create = true
				}
			}
			if p == nil {
				return nil, NewNoSuchElementErr("Pool exhausted")
			}
			if !p.Allocate() {
				p = nil
			}
		}
		if p != nil {
			e := pool.factory.ActivateObject(ctx, p)
			if e != nil {
				pool.destroy(ctx, p)
				p = nil
				if create {
					return nil, NewNoSuchElementErr("Unable to activate object")
				}
			}
		}
		if p != nil && (pool.Config.TestOnBorrow || create && pool.Config.TestOnCreate) {
			validate := pool.factory.ValidateObject(ctx, p)
			if !validate {
				pool.destroy(ctx, p)
				pool.destroyedByBorrowValidationCount.IncrementAndGet()
				p = nil
				if create {
					return nil, NewNoSuchElementErr("Unable to validate object")
				}
			}
		}
	}
	pool.updateStatsBorrow(p, time.Since(waitTime))
	return p.Object, nil
}
func (pool *ObjectPool) isAbandonedConfig() bool {
	return pool.AbandonedConfig != nil
}
func (pool *ObjectPool) ensureIdle(ctx context.Context, idleCount int, always bool) {
	if idleCount < 1 || pool.IsClosed() || (!always && !pool.idleObjects.HasTakeWaiters()) {
		return
	}
	for pool.idleObjects.Size() < idleCount {
		p, _ := pool.create(ctx)
		if p == nil {
			break
		}
		if pool.Config.LIFO {
			pool.idleObjects.AddFirst(p)
		} else {
			pool.idleObjects.AddLast(p)
		}
	}
	if pool.IsClosed() {
		pool.Clear(ctx)
	}
}
func (pool *ObjectPool) IsClosed() bool {
	pool.closeLock.Lock()
	defer pool.closeLock.Unlock()
	return pool.closed
}
func (pool *ObjectPool) ReturnObject(ctx context.Context, object interface{}) error {
	if object == nil {
		return errors.New("object is nil")
	}
	p, ok := pool.allObjects.Get(object).(*PooledObject)
	if !ok {
		if !pool.isAbandonedConfig() {
			return NewIllegalStateErr(
				"Returned object not currently part of pool")
		}
		return nil  
	}
	p.lock.Lock()
	state := p.state
	if state != StateAllocated {
		p.lock.Unlock()
		return NewIllegalStateErr(
			"Object has already been returned to pool or is invalid")
	}
	p.markReturning()  
	p.lock.Unlock()
	activeTime := p.GetActiveTime()
	if pool.Config.TestOnReturn {
		if !pool.factory.ValidateObject(ctx, p) {
			pool.destroy(ctx, p)
			pool.ensureIdle(ctx, 1, false)
			pool.updateStatsReturn(activeTime)
			return nil
		}
	}
	err := pool.factory.PassivateObject(ctx, p)
	if err != nil {
		pool.destroy(ctx, p)
		pool.ensureIdle(ctx, 1, false)
		pool.updateStatsReturn(activeTime)
		return nil
	}
	if !p.Deallocate() {
		return NewIllegalStateErr("Object has already been returned to pool or is invalid")
	}
	maxIdleSave := pool.Config.MaxIdle
	if pool.IsClosed() || maxIdleSave > -1 && maxIdleSave <= pool.idleObjects.Size() {
		pool.destroy(ctx, p)
	} else {
		if pool.Config.LIFO {
			pool.idleObjects.AddFirst(p)
		} else {
			pool.idleObjects.AddLast(p)
		}
		if pool.IsClosed() {
			pool.Clear(ctx)
		}
	}
	pool.updateStatsReturn(activeTime)
	return nil
}
func (pool *ObjectPool) Clear(ctx context.Context) {
	p, ok := pool.idleObjects.PollFirst().(*PooledObject)
	for ok {
		pool.destroy(ctx, p)
		p, ok = pool.idleObjects.PollFirst().(*PooledObject)
	}
}
func (pool *ObjectPool) InvalidateObject(ctx context.Context, object interface{}) error {
	p, ok := pool.allObjects.Get(object).(*PooledObject)
	if !ok {
		if pool.isAbandonedConfig() {
			return nil
		}
		return NewIllegalStateErr(
			"Invalidated object not currently part of pool")
	}
	p.lock.Lock()
	if p.state != StateInvalid {
		pool.doDestroy(ctx, p, true)
	}
	p.lock.Unlock()
	pool.ensureIdle(ctx, 1, false)
	return nil
}