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  }