github.com/hdt3213/godis@v1.2.9/lib/pool/pool.go (about)

     1  package pool
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  )
     7  
     8  var (
     9  	ErrClosed = errors.New("pool closed")
    10  	ErrMax    = errors.New("reach max connection limit")
    11  )
    12  
    13  type request chan interface{}
    14  
    15  type Config struct {
    16  	MaxIdle   uint
    17  	MaxActive uint
    18  }
    19  
    20  // Pool stores object for reusing, such as redis connection
    21  type Pool struct {
    22  	Config
    23  	factory     func() (interface{}, error)
    24  	finalizer   func(x interface{})
    25  	idles       chan interface{}
    26  	waitingReqs []request
    27  	activeCount uint // increases during creating connection, decrease during destroying connection
    28  	mu          sync.Mutex
    29  	closed      bool
    30  }
    31  
    32  func New(factory func() (interface{}, error), finalizer func(x interface{}), cfg Config) *Pool {
    33  	return &Pool{
    34  		factory:     factory,
    35  		finalizer:   finalizer,
    36  		idles:       make(chan interface{}, cfg.MaxIdle),
    37  		waitingReqs: make([]request, 0),
    38  		Config:      cfg,
    39  	}
    40  }
    41  
    42  // getOnNoIdle try to create a new connection or waiting for connection being returned
    43  // invoker should have pool.mu
    44  func (pool *Pool) getOnNoIdle() (interface{}, error) {
    45  	if pool.activeCount >= pool.MaxActive {
    46  		// waiting for connection being returned
    47  		req := make(chan interface{}, 1)
    48  		pool.waitingReqs = append(pool.waitingReqs, req)
    49  		pool.mu.Unlock()
    50  		x, ok := <-req
    51  		if !ok {
    52  			return nil, ErrMax
    53  		}
    54  		return x, nil
    55  	}
    56  
    57  	// create a new connection
    58  	pool.activeCount++ // hold a place for new connection
    59  	pool.mu.Unlock()
    60  	x, err := pool.factory()
    61  	if err != nil {
    62  		// create failed return token
    63  		pool.mu.Lock()
    64  		pool.activeCount-- // release the holding place
    65  		pool.mu.Unlock()
    66  		return nil, err
    67  	}
    68  	return x, nil
    69  }
    70  
    71  func (pool *Pool) Get() (interface{}, error) {
    72  	pool.mu.Lock()
    73  	if pool.closed {
    74  		pool.mu.Unlock()
    75  		return nil, ErrClosed
    76  	}
    77  
    78  	select {
    79  	case item := <-pool.idles:
    80  		pool.mu.Unlock()
    81  		return item, nil
    82  	default:
    83  		// no pooled item, create one
    84  		return pool.getOnNoIdle()
    85  	}
    86  }
    87  
    88  func (pool *Pool) Put(x interface{}) {
    89  	pool.mu.Lock()
    90  
    91  	if pool.closed {
    92  		pool.mu.Unlock()
    93  		pool.finalizer(x)
    94  		return
    95  	}
    96  
    97  	if len(pool.waitingReqs) > 0 {
    98  		req := pool.waitingReqs[0]
    99  		copy(pool.waitingReqs, pool.waitingReqs[1:])
   100  		pool.waitingReqs = pool.waitingReqs[:len(pool.waitingReqs)-1]
   101  		req <- x
   102  		pool.mu.Unlock()
   103  		return
   104  	}
   105  
   106  	select {
   107  	case pool.idles <- x:
   108  		pool.mu.Unlock()
   109  		return
   110  	default:
   111  		// reach max idle, destroy redundant item
   112  		pool.mu.Unlock()
   113  		pool.activeCount--
   114  		pool.finalizer(x)
   115  	}
   116  }
   117  
   118  func (pool *Pool) Close() {
   119  	pool.mu.Lock()
   120  	if pool.closed {
   121  		pool.mu.Unlock()
   122  		return
   123  	}
   124  	pool.closed = true
   125  	close(pool.idles)
   126  	pool.mu.Unlock()
   127  
   128  	for x := range pool.idles {
   129  		pool.finalizer(x)
   130  	}
   131  }