github.com/aaabigfish/gopkg@v1.1.0/gopool/pool.go (about)

     1  // Copyright 2021 ByteDance Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package gopool
    16  
    17  import (
    18  	"context"
    19  	"sync"
    20  	"sync/atomic"
    21  )
    22  
    23  type Pool interface {
    24  	// Name returns the corresponding pool name.
    25  	Name() string
    26  	// SetCap sets the goroutine capacity of the pool.
    27  	SetCap(cap int32)
    28  	// Go executes f.
    29  	Go(f func())
    30  	// CtxGo executes f and accepts the context.
    31  	CtxGo(ctx context.Context, f func())
    32  	// SetPanicHandler sets the panic handler.
    33  	SetPanicHandler(f func(context.Context, interface{}))
    34  	// WorkerCount returns the number of running workers
    35  	WorkerCount() int32
    36  }
    37  
    38  var taskPool sync.Pool
    39  
    40  func init() {
    41  	taskPool.New = newTask
    42  }
    43  
    44  type task struct {
    45  	ctx context.Context
    46  	f   func()
    47  
    48  	next *task
    49  }
    50  
    51  func (t *task) zero() {
    52  	t.ctx = nil
    53  	t.f = nil
    54  	t.next = nil
    55  }
    56  
    57  func (t *task) Recycle() {
    58  	t.zero()
    59  	taskPool.Put(t)
    60  }
    61  
    62  func newTask() interface{} {
    63  	return &task{}
    64  }
    65  
    66  type taskList struct {
    67  	sync.Mutex
    68  	taskHead *task
    69  	taskTail *task
    70  }
    71  
    72  type pool struct {
    73  	// The name of the pool
    74  	name string
    75  
    76  	// capacity of the pool, the maximum number of goroutines that are actually working
    77  	cap int32
    78  	// Configuration information
    79  	config *Config
    80  	// linked list of tasks
    81  	taskHead  *task
    82  	taskTail  *task
    83  	taskLock  sync.Mutex
    84  	taskCount int32
    85  
    86  	// Record the number of running workers
    87  	workerCount int32
    88  
    89  	// This method will be called when the worker panic
    90  	panicHandler func(context.Context, interface{})
    91  }
    92  
    93  // NewPool creates a new pool with the given name, cap and config.
    94  func NewPool(name string, cap int32, config *Config) Pool {
    95  	p := &pool{
    96  		name:   name,
    97  		cap:    cap,
    98  		config: config,
    99  	}
   100  	return p
   101  }
   102  
   103  func (p *pool) Name() string {
   104  	return p.name
   105  }
   106  
   107  func (p *pool) SetCap(cap int32) {
   108  	atomic.StoreInt32(&p.cap, cap)
   109  }
   110  
   111  func (p *pool) Go(f func()) {
   112  	p.CtxGo(context.Background(), f)
   113  }
   114  
   115  func (p *pool) CtxGo(ctx context.Context, f func()) {
   116  	t := taskPool.Get().(*task)
   117  	t.ctx = ctx
   118  	t.f = f
   119  	p.taskLock.Lock()
   120  	if p.taskHead == nil {
   121  		p.taskHead = t
   122  		p.taskTail = t
   123  	} else {
   124  		p.taskTail.next = t
   125  		p.taskTail = t
   126  	}
   127  	p.taskLock.Unlock()
   128  	atomic.AddInt32(&p.taskCount, 1)
   129  	// The following two conditions are met:
   130  	// 1. the number of tasks is greater than the threshold.
   131  	// 2. The current number of workers is less than the upper limit p.cap.
   132  	// or there are currently no workers.
   133  	if (atomic.LoadInt32(&p.taskCount) >= p.config.ScaleThreshold && p.WorkerCount() < atomic.LoadInt32(&p.cap)) || p.WorkerCount() == 0 {
   134  		p.incWorkerCount()
   135  		w := workerPool.Get().(*worker)
   136  		w.pool = p
   137  		w.run()
   138  	}
   139  }
   140  
   141  // SetPanicHandler the func here will be called after the panic has been recovered.
   142  func (p *pool) SetPanicHandler(f func(context.Context, interface{})) {
   143  	p.panicHandler = f
   144  }
   145  
   146  func (p *pool) WorkerCount() int32 {
   147  	return atomic.LoadInt32(&p.workerCount)
   148  }
   149  
   150  func (p *pool) incWorkerCount() {
   151  	atomic.AddInt32(&p.workerCount, 1)
   152  }
   153  
   154  func (p *pool) decWorkerCount() {
   155  	atomic.AddInt32(&p.workerCount, -1)
   156  }