github.com/zooyer/miskit@v1.0.71/utils/pool/pool.go (about) 1 package pool 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 ) 8 9 type Entry interface { 10 Ping() error 11 Close() error 12 } 13 14 type entry struct { 15 entry Entry 16 time time.Time 17 } 18 19 type Pool struct { 20 min int 21 max int 22 num int 23 idle time.Duration 24 new func() (entry Entry, err error) 25 entry chan entry 26 mutex sync.Mutex 27 } 28 29 func New(min, max int, idle time.Duration, new func() (entry Entry, err error)) *Pool { 30 var pool = &Pool{ 31 min: min, 32 max: max, 33 idle: idle, 34 new: new, 35 entry: make(chan entry, max), 36 } 37 38 return pool 39 } 40 41 func (p *Pool) ping(e entry) bool { 42 return e.entry.Ping() == nil 43 } 44 45 func (p *Pool) expired(e entry) bool { 46 return time.Now().After(e.time.Add(p.idle)) 47 } 48 49 func (p *Pool) add(n int) { 50 p.mutex.Lock() 51 defer p.mutex.Unlock() 52 p.num += n 53 } 54 55 func (p *Pool) less(n int, then func()) bool { 56 p.mutex.Lock() 57 defer p.mutex.Unlock() 58 59 if p.num+len(p.entry) < n { 60 then() 61 return true 62 } 63 64 return false 65 } 66 67 func (p *Pool) Get(ctx context.Context) (entry Entry, err error) { 68 for { 69 select { 70 case <-ctx.Done(): 71 return nil, ctx.Err() 72 case e := <-p.entry: 73 if p.expired(e) || !p.ping(e) { 74 _ = e.entry.Close() 75 continue 76 } 77 p.add(1) 78 return e.entry, nil 79 default: 80 if p.less(p.max, func() { 81 if entry, err = p.new(); err == nil { 82 p.num++ 83 } 84 }) { 85 return 86 } 87 select { 88 case <-ctx.Done(): 89 return nil, ctx.Err() 90 case e := <-p.entry: 91 p.add(1) 92 return e.entry, nil 93 } 94 } 95 } 96 } 97 98 func (p *Pool) Put(e Entry) (err error) { 99 defer p.add(-1) 100 101 if err = e.Ping(); err != nil { 102 return e.Close() 103 } 104 105 select { 106 case p.entry <- entry{entry: e, time: time.Now()}: 107 default: 108 return e.Close() 109 } 110 111 return 112 } 113 114 func (p *Pool) Len() int { 115 return len(p.entry) 116 }