github.com/searKing/golang/go@v1.2.74/exp/sync/fixedpool.go (about)

     1  // Copyright 2022 The searKing Author. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package sync
     6  
     7  import (
     8  	"context"
     9  	"runtime"
    10  	"sync"
    11  	"sync/atomic"
    12  	"time"
    13  
    14  	"github.com/searKing/golang/go/exp/container/queue"
    15  	math_ "github.com/searKing/golang/go/exp/math"
    16  	"github.com/searKing/golang/go/pragma"
    17  )
    18  
    19  const (
    20  	starvationThresholdNs = 1e6
    21  	localCMaxSize         = 16
    22  
    23  	UnlimitedResident = -1
    24  	UnlimitedCapacity = 0
    25  )
    26  
    27  // FixedPool is a set of resident and temporary items that may be individually saved and
    28  // retrieved.
    29  //
    30  // Any item stored in the Pool may be removed automatically at any time without
    31  // notification. If the Pool holds the only reference when this happens, the
    32  // item might be deallocated.
    33  //
    34  // A Pool is safe for use by multiple goroutines simultaneously.
    35  type FixedPool[E any] struct {
    36  	noCopy pragma.DoNotCopy
    37  
    38  	length   atomic.Int64 // items available
    39  	capacity atomic.Int64 // items allocated
    40  
    41  	// fixed-size pool for keep-alive
    42  	// localC + localQ
    43  	localC chan *FixedPoolElement[E] // fixed-size pool for keep-alive
    44  	mu     sync.Mutex
    45  	localQ queue.Queue[*FixedPoolElement[E]] // temporary pool for allocated, <keep-alive> excluded
    46  
    47  	factory       sync.Pool // specifies a function to generate a value
    48  	factoryVictim sync.Pool // A second GC should drop the victim cache, try put into local first.
    49  
    50  	pinChan chan struct{} // bell for Put or New
    51  
    52  	// New optionally specifies a function to generate
    53  	// a value when Get would otherwise return nil.
    54  	// It may not be changed concurrently with calls to Get.
    55  	New func() E
    56  
    57  	// MinResidentSize controls the minimum number of keep-alive items. items will be prealloced.
    58  	MinResidentSize int
    59  	// MaxResidentSize controls the maximum number of keep-alive items. Negative means no limit.
    60  	MaxResidentSize int
    61  	// MaxCapacity controls the maximum number of allocated items. Zero means no limit.
    62  	MaxCapacity int
    63  }
    64  
    65  // NewFixedPool returns an initialized fixed pool.
    66  // resident controls the maximum number of keep-alive items. Negative means no limit.
    67  // cap controls the maximum number of allocated items. Zero means no limit.
    68  func NewFixedPool[E any](f func() E, size int) *FixedPool[E] {
    69  	p := &FixedPool[E]{
    70  		New:             f,
    71  		MinResidentSize: size,
    72  		MaxResidentSize: size,
    73  		MaxCapacity:     size,
    74  	}
    75  	return p.Init()
    76  }
    77  
    78  // NewCachedPool Creates a pool that creates new items as needed, but
    79  // will reuse previously constructed items when they are available.
    80  // the pool will reuse previously constructed items and items will never be dropped.
    81  func NewCachedPool[E any](f func() E) *FixedPool[E] {
    82  	p := &FixedPool[E]{
    83  		New:             f,
    84  		MinResidentSize: 0,
    85  		MaxResidentSize: UnlimitedResident,
    86  		MaxCapacity:     UnlimitedCapacity,
    87  	}
    88  	return p.Init()
    89  }
    90  
    91  // NewTempPool Creates a pool that creates new items as needed, but
    92  // will be dropped at second GC if only referenced by the pool self.
    93  // the pool will reuse previously constructed items when they are available and not dropped.
    94  func NewTempPool[E any](f func() E) *FixedPool[E] {
    95  	p := &FixedPool[E]{
    96  		New:             f,
    97  		MinResidentSize: 0,
    98  		MaxResidentSize: 0,
    99  		MaxCapacity:     UnlimitedCapacity,
   100  	}
   101  	return p.Init()
   102  }
   103  
   104  // Init initializes fixed pool l.
   105  func (p *FixedPool[E]) Init() *FixedPool[E] {
   106  	if p.MaxResidentSize < 0 {
   107  		p.MaxCapacity = 0
   108  	} else {
   109  		p.MaxCapacity = math_.Max(p.MaxCapacity, p.MaxResidentSize, 0)
   110  	}
   111  	p.pinChan = make(chan struct{})
   112  
   113  	p.localC = make(chan *FixedPoolElement[E], math_.Min(localCMaxSize, math_.Max(p.MaxResidentSize, 0)))
   114  	if p.New != nil && p.factory.New == nil {
   115  		p.factory.New = func() any {
   116  			return newFixedPoolElement(p.New(), p)
   117  		}
   118  	}
   119  
   120  	p.preallocAllResident()
   121  	return p
   122  }
   123  
   124  func (p *FixedPool[E]) Finalize() {
   125  	// no need for a finalizer anymore
   126  	runtime.SetFinalizer(p, nil)
   127  }
   128  
   129  func (p *FixedPool[E]) preallocAllResident() {
   130  	if p.New != nil {
   131  		var xs []*FixedPoolElement[E]
   132  		for i := 0; i < p.MinResidentSize; i++ {
   133  			x := p.TryGet()
   134  			// short circuit
   135  			if x == nil {
   136  				break
   137  			}
   138  			xs = append(xs, x)
   139  		}
   140  		for i := 0; i < len(xs); i++ {
   141  			p.Put(xs[i])
   142  		}
   143  	}
   144  }
   145  
   146  func (p *FixedPool[E]) signal() {
   147  	select {
   148  	case p.pinChan <- struct{}{}:
   149  		break
   150  	default:
   151  		break
   152  	}
   153  }
   154  
   155  // Len returns the len of pool, that is object len idle, allocated and still in cache
   156  // The complexity is O(1).
   157  func (p *FixedPool[E]) Len() int {
   158  	return int(p.length.Load())
   159  }
   160  
   161  // Cap returns the capacity of pool, that is object len allocated
   162  // The complexity is O(1).
   163  func (p *FixedPool[E]) Cap() int {
   164  	return int(p.capacity.Load())
   165  }
   166  
   167  // Emplace adds x to the pool.
   168  // NOTE: Emplace may break through the len and cap boundaries, as x be allocated already.
   169  func (p *FixedPool[E]) Emplace(x E) {
   170  	p.Put(newFixedPoolElement(x, p).markAvailable(false))
   171  }
   172  
   173  // Put adds x to the pool.
   174  func (p *FixedPool[E]) Put(x *FixedPoolElement[E]) (stored bool) {
   175  	return p.put(x, true)
   176  }
   177  
   178  func (p *FixedPool[E]) TryPut(x *FixedPoolElement[E]) (stored bool) {
   179  	return p.put(x, false)
   180  }
   181  
   182  func (p *FixedPool[E]) put(x *FixedPoolElement[E], victim bool) (stored bool) {
   183  	if x == nil {
   184  		return
   185  	}
   186  	x.markAvailable(true)
   187  	defer p.signal()
   188  	select {
   189  	case p.localC <- x:
   190  		return true
   191  	default:
   192  		break
   193  	}
   194  	return p.putSlow(x, victim)
   195  }
   196  
   197  // Get selects an arbitrary item from the Pool, removes it from the
   198  // Pool, and returns it to the caller.
   199  // Get may choose to ignore the pool and treat it as empty.
   200  // Callers should not assume any relation between values passed to Put and
   201  // the values returned by Get.
   202  //
   203  // If Get would otherwise return nil and p.New is non-nil, Get returns
   204  // the result of calling p.New.
   205  //
   206  // Get uses context.Background internally; to specify the context, use
   207  // GetContext.
   208  func (p *FixedPool[E]) Get() *FixedPoolElement[E] {
   209  	e, _ := p.GetContext(context.Background())
   210  	return e
   211  }
   212  
   213  // GetContext selects an arbitrary item from the Pool, removes it from the
   214  // Pool, and returns it to the caller.
   215  // Get may choose to ignore the pool and treat it as empty.
   216  // Callers should not assume any relation between values passed to Put and
   217  // the values returned by Get.
   218  //
   219  // If GetContext would otherwise return nil and p.New is non-nil, Get returns
   220  // the result of calling p.New.
   221  func (p *FixedPool[E]) GetContext(ctx context.Context) (*FixedPoolElement[E], error) {
   222  	return p.get(ctx, -1)
   223  }
   224  
   225  func (p *FixedPool[E]) TryGet() *FixedPoolElement[E] {
   226  	e, _ := p.get(context.Background(), 1)
   227  	return e
   228  }
   229  
   230  func (p *FixedPool[E]) get(ctx context.Context, maxIter int) (*FixedPoolElement[E], error) {
   231  	select {
   232  	case e := <-p.localC:
   233  		return e.markAvailable(false), nil
   234  	case <-ctx.Done():
   235  		return nil, ctx.Err()
   236  	default:
   237  		if p.localQ.Next() {
   238  			p.mu.Lock()
   239  			if p.localQ.Next() {
   240  				e := p.localQ.PopFront()
   241  				p.mu.Unlock()
   242  				return e.markAvailable(false), nil
   243  			}
   244  			p.mu.Unlock()
   245  		}
   246  		break
   247  	}
   248  	return p.getSlow(ctx, maxIter)
   249  }
   250  
   251  func (p *FixedPool[E]) getSlow(ctx context.Context, maxIter int) (*FixedPoolElement[E], error) {
   252  	if ctx == nil {
   253  		panic("sync.FixedPool: nil Context")
   254  	}
   255  	timer := time.NewTimer(starvationThresholdNs)
   256  	defer timer.Stop()
   257  	iter := 0
   258  	for {
   259  		x, allocated := p.tryAllocate()
   260  		iter++
   261  		if allocated {
   262  			return x.markAvailable(false), nil
   263  		}
   264  
   265  		select {
   266  		case e := <-p.localC:
   267  			return e.markAvailable(false), nil
   268  		case <-ctx.Done():
   269  			return nil, ctx.Err()
   270  		default:
   271  			if p.localQ.Next() {
   272  				p.mu.Lock()
   273  				if p.localQ.Next() {
   274  					e := p.localQ.PopFront()
   275  					p.mu.Unlock()
   276  					return e.markAvailable(false), nil
   277  				}
   278  				p.mu.Unlock()
   279  			}
   280  			break
   281  		}
   282  		if maxIter > 0 && iter >= maxIter {
   283  			return nil, nil
   284  		}
   285  		if !timer.Stop() {
   286  			<-timer.C
   287  		}
   288  		timer.Reset(starvationThresholdNs)
   289  		select {
   290  		case e := <-p.localC:
   291  			return e.markAvailable(false), nil
   292  		case <-p.pinChan:
   293  			break
   294  		case <-ctx.Done():
   295  			return nil, ctx.Err()
   296  		case <-timer.C:
   297  			break
   298  		}
   299  	}
   300  }
   301  
   302  func (p *FixedPool[E]) putSlow(x *FixedPoolElement[E], victim bool) (stored bool) {
   303  	if x == nil {
   304  		return true
   305  	}
   306  
   307  	p.mu.Lock()
   308  	defer p.mu.Unlock()
   309  	x.markAvailable(true)
   310  	select {
   311  	case p.localC <- x:
   312  		return true
   313  	default:
   314  		break
   315  	}
   316  
   317  	move := p.moveToVictimLocked()
   318  	if move {
   319  		if victim {
   320  			// After one GC, the victim cache should keep them alive.
   321  			// A second GC should drop the victim cache.
   322  			p.factoryVictim.Put(x)
   323  			return true
   324  		}
   325  		return false
   326  	}
   327  	select {
   328  	case p.localC <- x:
   329  		return true
   330  	default:
   331  		p.localQ.PushBack(x)
   332  		return true
   333  	}
   334  }
   335  
   336  func (p *FixedPool[E]) isCapacityUnLimited() bool {
   337  	return p.MaxCapacity <= UnlimitedCapacity
   338  }
   339  func (p *FixedPool[E]) isResidentUnLimited() bool {
   340  	return p.MaxResidentSize <= UnlimitedResident
   341  }
   342  func (p *FixedPool[E]) moveToVictimLocked() bool {
   343  	// resident no limit
   344  	if p.isResidentUnLimited() {
   345  		return false
   346  	}
   347  	cap := p.Cap()
   348  	// cap and resident both has limit
   349  	if cap > p.MaxResidentSize {
   350  		return true
   351  	}
   352  	return false
   353  }
   354  
   355  func (p *FixedPool[E]) tryAllocate() (x *FixedPoolElement[E], allocated bool) {
   356  	// Try to pop the head of the victim for temporal locality of
   357  	// reuse.
   358  	{
   359  		x := p.factoryVictim.Get()
   360  		if x != nil {
   361  			return x.(*FixedPoolElement[E]), true
   362  		}
   363  	}
   364  
   365  	if p.MaxCapacity <= 0 || p.Cap() < p.MaxCapacity {
   366  		if p.Cap() < p.MaxCapacity {
   367  			x := p.factory.Get()
   368  			if x != nil {
   369  				return x.(*FixedPoolElement[E]), true
   370  			}
   371  		}
   372  		return nil, true
   373  	}
   374  	return nil, false
   375  }
   376  
   377  type FixedPoolElement[E any] struct {
   378  	// The value stored with this element.
   379  	Value E
   380  
   381  	available bool
   382  	pool      *FixedPool[E]
   383  }
   384  
   385  func newFixedPoolElement[E any](Value E, pool *FixedPool[E]) *FixedPoolElement[E] {
   386  	e := &FixedPoolElement[E]{
   387  		Value: Value,
   388  		pool:  pool,
   389  	}
   390  	e.pool.capacity.Add(1)
   391  	runtime.SetFinalizer(e, (*FixedPoolElement[E]).Finalize)
   392  	return e.markAvailable(true)
   393  }
   394  
   395  func (e *FixedPoolElement[E]) Finalize() {
   396  	stored := e.pool.putSlow(e, false)
   397  	if stored {
   398  		runtime.SetFinalizer(e, (*FixedPoolElement[E]).Finalize)
   399  		return
   400  	}
   401  	e.markAvailable(false).pool.capacity.Add(-1)
   402  	// no need for a finalizer anymore
   403  	runtime.SetFinalizer(e, nil)
   404  }
   405  
   406  func (e *FixedPoolElement[E]) Get() E {
   407  	if e == nil {
   408  		var zeroE E
   409  		return zeroE
   410  	}
   411  	return e.Value
   412  }
   413  
   414  func (e *FixedPoolElement[E]) markAvailable(available bool) *FixedPoolElement[E] {
   415  	if e != nil {
   416  		if available == e.available {
   417  			return e
   418  		}
   419  		e.available = available
   420  		if e.available {
   421  			e.pool.length.Add(1)
   422  		} else {
   423  			e.pool.length.Add(-1)
   424  		}
   425  	}
   426  	return e
   427  }