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