github.com/zhongdalu/gf@v1.0.0/g/container/gpool/gpool.go (about)

     1  // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  
     7  // Package gpool provides object-reusable concurrent-safe pool.
     8  package gpool
     9  
    10  import (
    11  	"errors"
    12  	"time"
    13  
    14  	"github.com/zhongdalu/gf/g/container/glist"
    15  	"github.com/zhongdalu/gf/g/container/gtype"
    16  	"github.com/zhongdalu/gf/g/os/gtime"
    17  	"github.com/zhongdalu/gf/g/os/gtimer"
    18  )
    19  
    20  // Object-Reusable Pool.
    21  type Pool struct {
    22  	list       *glist.List                 // Available/idle list.
    23  	closed     *gtype.Bool                 // Whether the pool is closed.
    24  	Expire     int64                       // Max idle time(ms), after which it is recycled.
    25  	NewFunc    func() (interface{}, error) // Callback function to create item.
    26  	ExpireFunc func(interface{})           // Expired destruction function for objects.
    27  	// This function needs to be defined when the pool object
    28  	// needs to perform additional destruction operations.
    29  	// Eg: net.Conn, os.File, etc.
    30  }
    31  
    32  // Pool item.
    33  type poolItem struct {
    34  	expire int64       // Expire time(millisecond).
    35  	value  interface{} // Value.
    36  }
    37  
    38  // Creation function for object.
    39  type NewFunc func() (interface{}, error)
    40  
    41  // Destruction function for object.
    42  type ExpireFunc func(interface{})
    43  
    44  // New returns a new object pool.
    45  // To ensure execution efficiency, the expiration time cannot be modified once it is set.
    46  //
    47  // Expiration logistics:
    48  // expire = 0 : not expired;
    49  // expire < 0 : immediate expired after use;
    50  // expire > 0 : timeout expired;
    51  // Note that the expiration time unit is ** milliseconds **.
    52  func New(expire int, newFunc NewFunc, expireFunc ...ExpireFunc) *Pool {
    53  	r := &Pool{
    54  		list:    glist.New(),
    55  		closed:  gtype.NewBool(),
    56  		Expire:  int64(expire),
    57  		NewFunc: newFunc,
    58  	}
    59  	if len(expireFunc) > 0 {
    60  		r.ExpireFunc = expireFunc[0]
    61  	}
    62  	gtimer.AddSingleton(time.Second, r.checkExpire)
    63  	return r
    64  }
    65  
    66  // Put puts an item to pool.
    67  func (p *Pool) Put(value interface{}) {
    68  	item := &poolItem{
    69  		value: value,
    70  	}
    71  	if p.Expire == 0 {
    72  		item.expire = 0
    73  	} else {
    74  		item.expire = gtime.Millisecond() + p.Expire
    75  	}
    76  	p.list.PushBack(item)
    77  }
    78  
    79  // Clear clears pool, which means it will remove all items from pool.
    80  func (p *Pool) Clear() {
    81  	p.list.RemoveAll()
    82  }
    83  
    84  // Get picks an item from pool.
    85  func (p *Pool) Get() (interface{}, error) {
    86  	for !p.closed.Val() {
    87  		if r := p.list.PopFront(); r != nil {
    88  			f := r.(*poolItem)
    89  			if f.expire == 0 || f.expire > gtime.Millisecond() {
    90  				return f.value, nil
    91  			}
    92  		} else {
    93  			break
    94  		}
    95  	}
    96  	if p.NewFunc != nil {
    97  		return p.NewFunc()
    98  	}
    99  	return nil, errors.New("pool is empty")
   100  }
   101  
   102  // Size returns the count of available items of pool.
   103  func (p *Pool) Size() int {
   104  	return p.list.Len()
   105  }
   106  
   107  // Close closes the pool. If <p> has ExpireFunc,
   108  // then it automatically closes all items using this function before it's closed.
   109  func (p *Pool) Close() {
   110  	p.closed.Set(true)
   111  }
   112  
   113  // checkExpire removes expired items from pool every second.
   114  func (p *Pool) checkExpire() {
   115  	if p.closed.Val() {
   116  		// If p has ExpireFunc,
   117  		// then it must close all items using this function.
   118  		if p.ExpireFunc != nil {
   119  			for {
   120  				if r := p.list.PopFront(); r != nil {
   121  					p.ExpireFunc(r.(*poolItem).value)
   122  				} else {
   123  					break
   124  				}
   125  			}
   126  		}
   127  		gtimer.Exit()
   128  	}
   129  	for {
   130  		// TODO Do not use Pop and Push mechanism, which is not graceful.
   131  		if r := p.list.PopFront(); r != nil {
   132  			item := r.(*poolItem)
   133  			if item.expire == 0 || item.expire > gtime.Millisecond() {
   134  				p.list.PushFront(item)
   135  				break
   136  			}
   137  			if p.ExpireFunc != nil {
   138  				p.ExpireFunc(item.value)
   139  			}
   140  		} else {
   141  			break
   142  		}
   143  	}
   144  }