github.com/songzhibin97/gkit@v1.2.13/container/pool/list.go (about)

     1  package pool
     2  
     3  import (
     4  	"container/list"
     5  	"context"
     6  	"sync"
     7  	"sync/atomic"
     8  	"time"
     9  
    10  	"github.com/songzhibin97/gkit/options"
    11  	"github.com/songzhibin97/gkit/timeout"
    12  )
    13  
    14  var _ Pool = &List{}
    15  
    16  // List 双向链表
    17  type List struct {
    18  	// f: item
    19  	f func(ctx context.Context) (IShutdown, error)
    20  
    21  	// mu: 互斥锁, 保护以下字段
    22  	mu sync.Mutex
    23  
    24  	// cond: 发送信号,通知有回收动作,在等待的可以再次尝试获取资源
    25  	cond chan struct{}
    26  
    27  	// cleanerCh: 清空 ch
    28  	cleanerCh chan struct{}
    29  
    30  	// active: 最大连接数
    31  	active uint64
    32  
    33  	// conf: 配置信息
    34  	conf *config
    35  
    36  	// closed:
    37  	closed uint32
    38  
    39  	// idles: 链表
    40  	idles list.List
    41  }
    42  
    43  // Reload 重新设置配置文件
    44  func (l *List) Reload(options ...options.Option) {
    45  	l.mu.Lock()
    46  	defer l.mu.Unlock()
    47  	for _, option := range options {
    48  		option(l.conf)
    49  	}
    50  }
    51  
    52  // Init 初始化
    53  func (l *List) Init(d time.Duration) {
    54  	// 如果 <= 0 放弃设置
    55  	if d <= 0 {
    56  		return
    57  	}
    58  	// 如果时间间隔d小于等待超时,并且 cleanerCh 不为nil 监听信号
    59  	if d < l.conf.idleTimeout && l.cleanerCh != nil {
    60  		select {
    61  		// 发送立即清除旧配置的信号,如果阻塞说明在时间周期内进行清洁,跳过
    62  		case l.cleanerCh <- struct{}{}:
    63  		default:
    64  		}
    65  	}
    66  	// 懒加载
    67  	if l.cleanerCh == nil {
    68  		l.cleanerCh = make(chan struct{}, 1)
    69  		// 开启定时任务
    70  		go l.Timer(l.conf.idleTimeout)
    71  	}
    72  }
    73  
    74  // Timer 定时任务
    75  func (l *List) Timer(d time.Duration) {
    76  	if d < minDuration {
    77  		d = minDuration
    78  	}
    79  	// ticker: 定时任务
    80  	ticker := time.NewTicker(d)
    81  	for {
    82  		select {
    83  		// 触发条件:
    84  		// 1. 定时周期
    85  		// 2. l.cleanerCh 接收到信号
    86  		case <-ticker.C:
    87  		case <-l.cleanerCh:
    88  		}
    89  		l.mu.Lock()
    90  		// 是否关闭 或者 没有设置超时时间
    91  		if atomic.LoadUint32(&l.closed) == 1 || l.conf.idleTimeout <= 0 {
    92  			l.mu.Unlock()
    93  			return
    94  		}
    95  		// 循环链表
    96  		for i, n := 0, l.idles.Len(); i < n; i++ {
    97  			// idles.Back() 返回链表中最后一个元素, 如果当前链表已经是空了 则返回nil
    98  			e := l.idles.Back()
    99  			if e == nil {
   100  				break
   101  			}
   102  			// 断言为 item
   103  			ic := e.Value.(item)
   104  			// 判断时间是否超时
   105  			if !ic.expire(l.conf.idleTimeout) {
   106  				break
   107  			}
   108  			// 如果已经超时,则删除此元素
   109  			l.idles.Remove(e)
   110  			// release 计数
   111  			l.release()
   112  			l.mu.Unlock()
   113  			_ = ic.s.Shutdown()
   114  			l.mu.Lock()
   115  		}
   116  		l.mu.Unlock()
   117  	}
   118  }
   119  
   120  // release 当前活跃线程数-1 并发送信号通知
   121  // hold p.mu during the call.
   122  func (l *List) release() {
   123  	// l.active -= 1
   124  	atomic.AddUint64(&l.active, ^uint64(0))
   125  	l.signal()
   126  }
   127  
   128  // signal 发送信号通知
   129  func (l *List) signal() {
   130  	select {
   131  	case l.cond <- struct{}{}:
   132  	default:
   133  	}
   134  }
   135  
   136  // Get 获取
   137  func (l *List) Get(ctx context.Context) (IShutdown, error) {
   138  	l.mu.Lock()
   139  	// 判断是否关闭
   140  	if atomic.LoadUint32(&l.closed) == 1 {
   141  		l.mu.Unlock()
   142  		return nil, ErrPoolClosed
   143  	}
   144  	for {
   145  		for i, n := 0, l.idles.Len(); i < n; i++ {
   146  			e := l.idles.Front()
   147  			if e == nil {
   148  				break
   149  			}
   150  			ic := e.Value.(item)
   151  			l.idles.Remove(e)
   152  			l.mu.Unlock()
   153  			// 没有过期的可以直接返回了
   154  			if !ic.expire(l.conf.idleTimeout) {
   155  				return ic.s, nil
   156  			}
   157  			// 清理 重新获取锁
   158  			_ = ic.s.Shutdown()
   159  			l.mu.Lock()
   160  			l.release()
   161  		}
   162  
   163  		// 检查是否关闭
   164  		if atomic.LoadUint32(&l.closed) == 1 {
   165  			l.mu.Unlock()
   166  			return nil, ErrPoolClosed
   167  		}
   168  		// 判断是否需要新增
   169  		if l.conf.active == 0 || l.active < l.conf.active {
   170  			if l.f == nil {
   171  				return nil, ErrPoolNewFuncIsNull
   172  			}
   173  			newItem := l.f
   174  			l.mu.Unlock()
   175  			atomic.AddUint64(&l.active, 1)
   176  			// 新增:
   177  			c, err := newItem(ctx)
   178  			if err != nil {
   179  				l.release()
   180  				c = nil
   181  			}
   182  			return c, err
   183  		}
   184  		// 如果满了判断是否需要等待
   185  		if l.conf.waitTimeout == 0 && !l.conf.wait {
   186  			l.mu.Unlock()
   187  			return nil, ErrPoolExhausted
   188  		}
   189  		// 获取超时时间,解锁进入等待状态
   190  		wt := l.conf.waitTimeout
   191  		l.mu.Unlock()
   192  
   193  		// 控制链路超时时间
   194  		_, nCtx, cancel := timeout.Shrink(ctx, wt)
   195  
   196  		// 超时/收到了某应用回收的信号
   197  		select {
   198  		case <-nCtx.Done():
   199  			cancel()
   200  			return nil, nCtx.Err()
   201  		case <-l.cond:
   202  		}
   203  		// 自旋,再次尝试获得句柄
   204  		cancel()
   205  		l.mu.Lock()
   206  	}
   207  }
   208  
   209  // Put 回收
   210  func (l *List) Put(ctx context.Context, s IShutdown, forceClose bool) error {
   211  	l.mu.Lock()
   212  	if atomic.LoadUint32(&l.closed) == 0 && !forceClose {
   213  		// 插入到链表头
   214  		l.idles.PushFront(item{createdAt: nowFunc(), s: s})
   215  		// 判断闲置数量是否达到阈值
   216  		if uint64(l.idles.Len()) > l.conf.idle {
   217  			// 拿到尾部淘汰的 shutdown
   218  			s = l.idles.Remove(l.idles.Back()).(item).s
   219  		} else {
   220  			s = nil
   221  		}
   222  	}
   223  	// 如果 s == nil 进入回收
   224  	if s == nil {
   225  		l.signal()
   226  		l.mu.Unlock()
   227  		return nil
   228  	}
   229  	l.mu.Unlock()
   230  	l.release()
   231  	return s.Shutdown()
   232  }
   233  
   234  // Shutdown 关闭
   235  func (l *List) Shutdown() error {
   236  	l.mu.Lock()
   237  	if atomic.SwapUint32(&l.closed, 1) == 1 {
   238  		return ErrPoolClosed
   239  	}
   240  	idles := l.idles
   241  	// .Init 重新初始化链表 快速清空
   242  	l.idles.Init()
   243  	if idles.Len() > 0 {
   244  		atomic.AddUint64(&l.active, ^uint64(idles.Len()-1))
   245  	}
   246  	l.mu.Unlock()
   247  	// 在循环旧链表进行资源回收
   248  	for e := idles.Front(); e != nil; e = e.Next() {
   249  		_ = e.Value.(item).s.Shutdown()
   250  	}
   251  	return nil
   252  }
   253  
   254  // New 设置创建资源函数
   255  func (l *List) New(f func(ctx context.Context) (IShutdown, error)) {
   256  	l.mu.Lock()
   257  	defer l.mu.Unlock()
   258  	l.f = f
   259  }
   260  
   261  // NewList 实例化
   262  func NewList(options ...options.Option) Pool {
   263  	l := &List{conf: defaultConfig()}
   264  	l.cond = make(chan struct{})
   265  	for _, option := range options {
   266  		option(l.conf)
   267  	}
   268  	l.Init(l.conf.idleTimeout)
   269  	return l
   270  }