github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/src/x/sync/types.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package sync
    22  
    23  import (
    24  	gocontext "context"
    25  	"time"
    26  
    27  	"github.com/m3db/m3/src/x/context"
    28  	"github.com/m3db/m3/src/x/instrument"
    29  )
    30  
    31  // Work is a unit of item to be worked on.
    32  type Work func()
    33  
    34  // PooledWorkerPool provides a pool for goroutines, but unlike WorkerPool,
    35  // the actual goroutines themselves are re-used. This can be useful from a
    36  // performance perspective in scenarios where the allocation and growth of
    37  // the new goroutine and its stack is a bottleneck. Specifically, if the
    38  // work function being performed has a very deep call-stack, calls to
    39  // runtime.morestack can dominate the workload. Re-using existing goroutines
    40  // allows the stack to be grown once, and then re-used for many invocations.
    41  //
    42  // In order to prevent abnormally large goroutine stacks from persisting over
    43  // the life-cycle of an application, the PooledWorkerPool will randomly kill
    44  // existing goroutines and spawn a new one.
    45  //
    46  // The PooledWorkerPool also implements sharding of its underlying worker channels
    47  // to prevent excessive lock contention.
    48  type PooledWorkerPool interface {
    49  	// Init initializes the pool.
    50  	Init()
    51  
    52  	// Go assign the Work to be executed by a Goroutine. Whether or not
    53  	// it waits for an existing Goroutine to become available or not
    54  	// is determined by the GrowOnDemand() option. If GrowOnDemand is not
    55  	// set then the call to Go() will block until a goroutine is available.
    56  	// If GrowOnDemand() is set then it will expand the pool of goroutines to
    57  	// accommodate the work. The newly allocated goroutine will temporarily
    58  	// participate in the pool in an effort to amortize its allocation cost, but
    59  	// will eventually be killed. This allows the pool to dynamically respond to
    60  	// workloads without causing excessive memory pressure. The pool will grow in
    61  	// size when the workload exceeds its capacity and shrink back down to its
    62  	// original size if/when the burst subsides.
    63  	Go(work Work)
    64  
    65  	// GoWithTimeout waits up to the given timeout for a worker to become
    66  	// available, returning true if a worker becomes available, or false
    67  	// otherwise.
    68  	GoWithTimeout(work Work, timeout time.Duration) bool
    69  
    70  	// GoWithContext waits until a worker is available or the provided ctx is
    71  	// canceled.
    72  	GoWithContext(ctx gocontext.Context, work Work) bool
    73  
    74  	// FastContextCheck returns a wrapper worker pool that only checks the context deadline every batchSize calls.
    75  	// This is useful for tight looping code that wants to amortize the cost of the ctx deadline check over batchSize
    76  	// iterations.
    77  	// This should only be used for code that can guarantee the wait time for a worker is low since if the ctx is not
    78  	// checked the calling goroutine blocks waiting for a worker.
    79  	FastContextCheck(batchSize int) PooledWorkerPool
    80  }
    81  
    82  // NewPooledWorkerOptions is a set of new instrument worker pool options.
    83  type NewPooledWorkerOptions struct {
    84  	InstrumentOptions instrument.Options
    85  }
    86  
    87  // NewPooledWorkerFn returns a pooled worker pool that Init must be called on.
    88  type NewPooledWorkerFn func(opts NewPooledWorkerOptions) (PooledWorkerPool, error)
    89  
    90  // WorkerPool provides a pool for goroutines.
    91  type WorkerPool interface {
    92  	// Init initializes the pool.
    93  	Init()
    94  
    95  	// Go waits until the next worker becomes available and executes it.
    96  	Go(work Work)
    97  
    98  	// GoInstrument instruments Go with timing information.
    99  	GoInstrument(work Work) ScheduleResult
   100  
   101  	// GoIfAvailable performs the work inside a worker if one is available and
   102  	// returns true, or false otherwise.
   103  	GoIfAvailable(work Work) bool
   104  
   105  	// GoWithTimeout waits up to the given timeout for a worker to become
   106  	// available, returning true if a worker becomes available, or false
   107  	// otherwise.
   108  	GoWithTimeout(work Work, timeout time.Duration) bool
   109  
   110  	// GoWithTimeoutInstrument instruments GoWithTimeout with timing information.
   111  	GoWithTimeoutInstrument(work Work, timeout time.Duration) ScheduleResult
   112  
   113  	// GoWithContext waits until a worker is available or the provided ctx is canceled.
   114  	GoWithContext(ctx context.Context, work Work) ScheduleResult
   115  
   116  	// FastContextCheck returns a wrapper worker pool that only checks the context deadline every batchSize calls.
   117  	// This is useful for tight looping code that wants to amortize the cost of the ctx deadline check over batchSize
   118  	// iterations.
   119  	// This should only be used for code that can guarantee the wait time for a worker is low since if the ctx is not
   120  	// checked the calling goroutine blocks waiting for a worker.
   121  	FastContextCheck(batchSize int) WorkerPool
   122  
   123  	// Size returns the size of the worker pool.
   124  	Size() int
   125  }
   126  
   127  // ScheduleResult is the result of scheduling a goroutine in the worker pool.
   128  type ScheduleResult struct {
   129  	// Available is true if the goroutine was scheduled in the worker pool. False if the request timed out before a
   130  	// worker became available.
   131  	Available bool
   132  	// WaitTime is how long the goroutine had to wait before receiving a worker from the pool or timing out.
   133  	WaitTime time.Duration
   134  }
   135  
   136  // PooledWorkerPoolOptions is the options for a PooledWorkerPool.
   137  type PooledWorkerPoolOptions interface {
   138  	// SetGrowOnDemand sets whether the GrowOnDemand feature is enabled.
   139  	SetGrowOnDemand(value bool) PooledWorkerPoolOptions
   140  
   141  	// GrowOnDemand returns whether the GrowOnDemand feature is enabled.
   142  	GrowOnDemand() bool
   143  
   144  	// SetNumShards sets the number of worker channel shards.
   145  	SetNumShards(value int64) PooledWorkerPoolOptions
   146  
   147  	// NumShards returns the number of worker channel shards.
   148  	NumShards() int64
   149  
   150  	// SetKillWorkerProbability sets the probability to kill a worker.
   151  	SetKillWorkerProbability(value float64) PooledWorkerPoolOptions
   152  
   153  	// KillWorkerProbability returns the probability to kill a worker.
   154  	KillWorkerProbability() float64
   155  
   156  	// SetNowFn sets the now function.
   157  	SetNowFn(value NowFn) PooledWorkerPoolOptions
   158  
   159  	// NowFn returns the now function.
   160  	NowFn() NowFn
   161  
   162  	// SetInstrumentOptions sets the instrument options.
   163  	SetInstrumentOptions(value instrument.Options) PooledWorkerPoolOptions
   164  
   165  	// InstrumentOptions returns the now function.
   166  	InstrumentOptions() instrument.Options
   167  }