github.com/lirm/aeron-go@v0.0.0-20230415210743-920325491dc4/aeron/idlestrategy/backoffidlestrategy.go (about)

     1  package idlestrategy
     2  
     3  import (
     4  	"fmt"
     5  	"runtime"
     6  	"time"
     7  )
     8  
     9  // BackoffIdleStrategy is an idling strategy for threads when they have no work to do.
    10  // Spin for maxSpins, then yield with runtime.Gosched for maxYields,
    11  // then park with time.Sleep on an exponential backoff to maxParkPeriodNs.
    12  type BackoffIdleStrategy struct {
    13  	// configured max spins, yield, and min / max park period
    14  	maxSpins, maxYields, minParkPeriodNs, maxParkPeriodNs int64
    15  	// current state
    16  	state backoffState
    17  	// current number of spins, yield, and park period.
    18  	spins, yields, parkPeriodNs int64
    19  }
    20  
    21  // NewBackoffIdleStrategy returns a BackoffIdleStrategy with the given parameters.
    22  func NewBackoffIdleStrategy(maxSpins, maxYields, minParkPeriodNs, maxParkPeriodNs int64) *BackoffIdleStrategy {
    23  	return &BackoffIdleStrategy{
    24  		maxSpins:        maxSpins,
    25  		maxYields:       maxYields,
    26  		minParkPeriodNs: minParkPeriodNs,
    27  		maxParkPeriodNs: maxParkPeriodNs,
    28  	}
    29  }
    30  
    31  // DefaultMaxSpins is the default number of times the strategy will spin without work before going to next state.
    32  const DefaultMaxSpins = 10
    33  
    34  // DefaultMaxYields is the default number of times the strategy will yield without work before going to next state.
    35  const DefaultMaxYields = 20
    36  
    37  // DefaultMinParkNs is the default interval the strategy will park the thread on entering the park state.
    38  const DefaultMinParkNs = 1000
    39  
    40  // DefaultMaxParkNs is the default interval the strategy will park the thread will expand interval to as a max.
    41  const DefaultMaxParkNs = int64(1 * time.Millisecond)
    42  
    43  // NewDefaultBackoffIdleStrategy returns a BackoffIdleStrategy using DefaultMaxSpins, DefaultMaxYields,
    44  // DefaultMinParkNs, and DefaultMaxParkNs.
    45  func NewDefaultBackoffIdleStrategy() *BackoffIdleStrategy {
    46  	return NewBackoffIdleStrategy(DefaultMaxSpins, DefaultMaxYields, DefaultMinParkNs, DefaultMaxParkNs)
    47  }
    48  
    49  type backoffState int8
    50  
    51  const (
    52  	// Denotes a non-idle state.
    53  	backoffNotIdle backoffState = iota
    54  	// Denotes a spinning state.
    55  	backoffSpinning
    56  	// Denotes an yielding state.
    57  	backoffYielding
    58  	// Denotes a parking state.
    59  	backoffParking
    60  )
    61  
    62  func (s *BackoffIdleStrategy) Idle(workCount int) {
    63  	if workCount > 0 {
    64  		s.reset()
    65  	} else {
    66  		s.idle()
    67  	}
    68  }
    69  
    70  func (s *BackoffIdleStrategy) String() string {
    71  	return fmt.Sprintf("BackoffIdleStrategy(MaxSpins:%d, MaxYields:%d, MinParkPeriodNs:%d, MaxParkPeriodNs:%d)",
    72  		s.maxSpins, s.maxYields, s.minParkPeriodNs, s.maxParkPeriodNs)
    73  }
    74  
    75  func (s *BackoffIdleStrategy) reset() {
    76  	s.spins = 0
    77  	s.yields = 0
    78  	s.parkPeriodNs = s.minParkPeriodNs
    79  	s.state = backoffNotIdle
    80  }
    81  
    82  func (s *BackoffIdleStrategy) idle() {
    83  	switch s.state {
    84  	case backoffNotIdle:
    85  		s.state = backoffSpinning
    86  		s.spins++
    87  	case backoffSpinning:
    88  		// We should call procyield here, see https://golang.org/src/runtime/lock_futex.go
    89  		s.spins++
    90  		if s.spins > s.maxSpins {
    91  			s.state = backoffYielding
    92  			s.yields = 0
    93  		}
    94  	case backoffYielding:
    95  		s.yields++
    96  		if s.yields > s.maxYields {
    97  			s.state = backoffParking
    98  			s.parkPeriodNs = s.minParkPeriodNs
    99  		} else {
   100  			runtime.Gosched()
   101  		}
   102  	case backoffParking:
   103  		time.Sleep(time.Nanosecond * time.Duration(s.parkPeriodNs))
   104  		s.parkPeriodNs = s.parkPeriodNs << 1
   105  		if s.parkPeriodNs > s.maxParkPeriodNs {
   106  			s.parkPeriodNs = s.maxParkPeriodNs
   107  		}
   108  	}
   109  }