github.com/searKing/golang/go@v1.2.74/time/rate/rate.go (about)

     1  // Copyright 2020 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 rate
     6  // The key observation and some code is borrowed from
     7  // golang.org/x/time/rate/rate.go
     8  package rate
     9  
    10  import (
    11  	"context"
    12  	"fmt"
    13  	"sync"
    14  	"time"
    15  )
    16  
    17  const (
    18  	starvationThresholdNs = 1e6
    19  )
    20  
    21  type expectKeyType struct{}
    22  
    23  var expectTokensKey expectKeyType
    24  
    25  // A BurstLimiter controls how frequently events are allowed to happen.
    26  // It implements a "token bucket" of size b, initially full and refilled
    27  // by PutToken or PutTokenN.
    28  
    29  // BurstLimiter
    30  // Informally, in any large enough time interval, the BurstLimiter limits the
    31  // burst tokens, with a maximum burst size of b events.
    32  // As a special case, if r == Inf (the infinite rate), b is ignored.
    33  // See https://en.wikipedia.org/wiki/Token_bucket for more about token buckets.
    34  //
    35  // Reorder Buffer
    36  // It allows instructions to be committed in-order.
    37  // - Allocated by `Reserve`  or `ReserveN` into account when allowing future events
    38  // - Wait by `Wait` or `WaitN` blocks until lim permits n events to happen.
    39  // - Allow and Wait Complete by `PutToken` or `PutTokenN`
    40  // - Reserve Complete by `Cancel` of the Reservation self, GC Cancel supported
    41  // See https://en.wikipedia.org/wiki/Re-order_buffer for more about Reorder buffer.
    42  // See https://web.archive.org/web/20040724215416/http://lgjohn.okstate.edu/6253/lectures/reorder.pdf for more about Reorder buffer.
    43  //
    44  // The zero value is a valid BurstLimiter, but it will reject all events.
    45  // Use NewFullBurstLimiter to create non-zero Limiters.
    46  //
    47  // BurstLimiter has three main methods, Allow, Reserve, and Wait.
    48  // Most callers should use Wait for token bucket.
    49  // Most callers should use Reserve for Reorder buffer.
    50  //
    51  // Each of the three methods consumes a single token.
    52  // They differ in their behavior when no token is available.
    53  // If no token is available, Allow returns false.
    54  // If no token is available, Reserve returns a reservation for a future token
    55  // and the amount of time the caller must wait before using it.
    56  // If no token is available, Wait blocks until one can be obtained
    57  // or its associated context.Context is canceled.
    58  //
    59  // The methods AllowN, ReserveN, and WaitN consume n tokens.
    60  type BurstLimiter struct {
    61  	mu                     sync.Mutex
    62  	burst                  int // bucket size, Put Must be called after Get
    63  	tokensChangedListeners []context.Context
    64  
    65  	tokens int // unconsumed tokens
    66  }
    67  
    68  // Burst returns the maximum burst size. Burst is the maximum number of tokens
    69  // that can be consumed in a single call to Allow, Reserve, or Wait, so higher
    70  // Burst values allow more events to happen at once.
    71  // A zero Burst allows no events, unless limit == Inf.
    72  func (lim *BurstLimiter) Burst() int {
    73  	lim.mu.Lock()
    74  	defer lim.mu.Unlock()
    75  	return lim.burst
    76  }
    77  
    78  // Tokens returns the token nums unconsumed.
    79  func (lim *BurstLimiter) Tokens() int {
    80  	lim.mu.Lock()
    81  	defer lim.mu.Unlock()
    82  	return lim.tokens
    83  }
    84  
    85  // NewFullBurstLimiter returns a new BurstLimiter inited with full tokens that allows
    86  // events up to burst b and permits bursts of at most b tokens.
    87  func NewFullBurstLimiter(b int) *BurstLimiter {
    88  	return &BurstLimiter{
    89  		burst:  b,
    90  		tokens: b,
    91  	}
    92  }
    93  
    94  // NewEmptyBurstLimiter returns a new BurstLimiter inited with zero tokens that allows
    95  // events up to burst b and permits bursts of at most b tokens.
    96  func NewEmptyBurstLimiter(b int) *BurstLimiter {
    97  	return &BurstLimiter{
    98  		burst: b,
    99  	}
   100  }
   101  
   102  // SetBurst sets a new burst size for the limiter.
   103  func (lim *BurstLimiter) SetBurst(newBurst int) {
   104  	lim.mu.Lock()
   105  	defer lim.mu.Unlock()
   106  	lim.burst = newBurst
   107  }
   108  
   109  // Allow is shorthand for AllowN(time.Now(), 1).
   110  // 当没有可用或足够的事件时,返回false
   111  func (lim *BurstLimiter) Allow() bool {
   112  	return lim.AllowN(1)
   113  }
   114  
   115  // AllowN reports whether n events may happen at time now.
   116  // AllowN is shorthand for GetTokenN.
   117  // Use this method if you intend to drop / skip events that exceed the rate limit.
   118  // Otherwise, use Reserve or Wait.
   119  // 当没有可用或足够的事件时,返回false
   120  func (lim *BurstLimiter) AllowN(n int) bool {
   121  	return lim.GetTokenN(n)
   122  }
   123  
   124  // Reserve is shorthand for ReserveN(1).
   125  // 当没有可用或足够的事件时,返回 Reservation,和要等待多久才能获得足够的事件。
   126  func (lim *BurstLimiter) Reserve(ctx context.Context) *Reservation {
   127  	return lim.ReserveN(ctx, 1)
   128  }
   129  
   130  // ReserveN returns a Reservation that indicates how long the caller must wait before n events happen.
   131  // The BurstLimiter takes this Reservation into account when allowing future events.
   132  // ReserveN returns false if n exceeds the BurstLimiter's burst size.
   133  // Usage example:
   134  //
   135  //	    // Allocate: The dispatch stage reserves space in the reorder buffer for instructions in program order.
   136  //		r := lim.ReserveN(context.Background(), 1)
   137  //		if !r.OK() {
   138  //			// Not allowed to act! Did you remember to set lim.burst to be > 0 ?
   139  //			return
   140  //		}
   141  //
   142  //		// Execute: out-of-order execution
   143  //		Act()
   144  //
   145  //		// Wait: The complete stage must wait for instructions to finish execution.
   146  //		if err:= r.Wait(); err!=nil {
   147  //		// Not allowed to act! Reservation or context canceled ?
   148  //			return
   149  //		}
   150  //		// Complete: Finished instructions are allowed to write results in order into the architected registers.
   151  //		// It allows instructions to be committed in-order.
   152  //		defer r.PutToken()
   153  //
   154  //		// Execute: in-order execution
   155  //		Act()
   156  //
   157  // Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events.
   158  // If you need to respect a deadline or cancel the delay, use Wait instead.
   159  // To drop or skip events exceeding rate limit, use Allow instead.
   160  // 当没有可用或足够的事件时,返回 Reservation,和要等待多久才能获得足够的事件。
   161  // See https://en.wikipedia.org/wiki/Re-order_buffer for more about Reorder buffer.
   162  // See https://web.archive.org/web/20040724215416/http://lgjohn.okstate.edu/6253/lectures/reorder.pdf for more about Reorder buffer.
   163  func (lim *BurstLimiter) ReserveN(ctx context.Context, n int) *Reservation {
   164  	r := lim.reserveN(ctx, n, true, true)
   165  	return r
   166  }
   167  
   168  // Wait is shorthand for WaitN(ctx, 1).
   169  func (lim *BurstLimiter) Wait(ctx context.Context) (err error) {
   170  	return lim.WaitN(ctx, 1)
   171  }
   172  
   173  // WaitN blocks until lim permits n events to happen.
   174  // It returns an error if n exceeds the BurstLimiter's burst size, the Context is
   175  // canceled, or the expected wait time exceeds the Context's Deadline.
   176  // The burst limit is ignored if the rate limit is Inf.
   177  func (lim *BurstLimiter) WaitN(ctx context.Context, n int) (err error) {
   178  	lim.mu.Lock()
   179  	burst := lim.burst
   180  	lim.mu.Unlock()
   181  
   182  	if n > burst {
   183  		return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, burst)
   184  	}
   185  	// Check if ctx is already cancelled
   186  	select {
   187  	case <-ctx.Done():
   188  		return ctx.Err()
   189  	default:
   190  	}
   191  	// Reserve
   192  	r := lim.reserveN(ctx, n, true, false)
   193  	if r.Ready() { // tokens already hold by the Reservation
   194  		return nil
   195  	}
   196  
   197  	// Wait if necessary
   198  	return r.Wait(ctx)
   199  }
   200  
   201  // PutToken is shorthand for PutTokenN(ctx, 1).
   202  func (lim *BurstLimiter) PutToken() {
   203  	lim.PutTokenN(1)
   204  }
   205  
   206  func (lim *BurstLimiter) PutTokenN(n int) {
   207  	lim.mu.Lock()
   208  	defer lim.mu.Unlock()
   209  	lim.tokens += n
   210  	// drop if overflowed
   211  	if lim.tokens > lim.burst {
   212  		lim.tokens = lim.burst
   213  	}
   214  
   215  	for i := 0; i < len(lim.tokensChangedListeners); i++ {
   216  		tokensGot := lim.tokensChangedListeners[i]
   217  		r := tokensGot.Value(expectTokensKey).(*reservation)
   218  		if r.burst <= 0 {
   219  			// remove notified
   220  			if i == len(lim.tokensChangedListeners)-1 {
   221  				lim.tokensChangedListeners = lim.tokensChangedListeners[:i]
   222  			} else {
   223  				lim.tokensChangedListeners = append(lim.tokensChangedListeners[:i], lim.tokensChangedListeners[i+1:]...)
   224  			}
   225  			r.notifyTokensReady()
   226  			continue
   227  		}
   228  
   229  		tokensWait := r.burst - r.tokens
   230  
   231  		// tokens in the Bucket is not enough for the Reservation
   232  		if lim.tokens < tokensWait {
   233  			r.tokens += lim.tokens
   234  			lim.tokens = 0
   235  			break
   236  		}
   237  
   238  		// enough
   239  		r.tokens = r.burst
   240  		lim.tokens -= tokensWait
   241  		// remove notified
   242  		if i == len(lim.tokensChangedListeners)-1 {
   243  			lim.tokensChangedListeners = lim.tokensChangedListeners[:i]
   244  		} else {
   245  			lim.tokensChangedListeners = append(lim.tokensChangedListeners[:i], lim.tokensChangedListeners[i+1:]...)
   246  		}
   247  		r.notifyTokensReady()
   248  		continue
   249  	}
   250  }
   251  
   252  // GetToken is shorthand for GetTokenN(ctx, 1).
   253  func (lim *BurstLimiter) GetToken() (ok bool) {
   254  	return lim.GetTokenN(1)
   255  }
   256  
   257  // GetTokenN returns true if token is got
   258  func (lim *BurstLimiter) GetTokenN(n int) (ok bool) {
   259  	lim.mu.Lock()
   260  	defer lim.mu.Unlock()
   261  	return lim.getTokenNLocked(n)
   262  }
   263  
   264  // getTokenNLocked returns true if token is got
   265  // advance calculates and returns an updated state for lim resulting from the passage of time.
   266  // lim is not changed.
   267  // getTokenNLocked requires that lim.mu is held.
   268  func (lim *BurstLimiter) getTokenNLocked(n int) (ok bool) {
   269  	if n <= 0 {
   270  		return true
   271  	}
   272  	if lim.tokens >= n {
   273  		lim.tokens -= n
   274  		return true
   275  	}
   276  	return false
   277  }
   278  
   279  // reserveN is a helper method for AllowN, ReserveN, and WaitN.
   280  // maxFutureReserve specifies the maximum reservation wait duration allowed.
   281  // reserveN returns Reservation, not *reservation, to avoid allocation in AllowN and WaitN.
   282  func (lim *BurstLimiter) reserveN(ctx context.Context, n int, wait bool, gc bool) *Reservation {
   283  	if n <= 0 {
   284  		r := newReservation(gc)
   285  		r.lim = lim
   286  		r.burst = 0
   287  		r.tokens = 0
   288  		return r
   289  	}
   290  
   291  	lim.mu.Lock()
   292  	defer lim.mu.Unlock()
   293  
   294  	// tokens are enough
   295  	if lim.tokens >= n && len(lim.tokensChangedListeners) == 0 {
   296  		// get n tokens from lim
   297  		if lim.getTokenNLocked(n) {
   298  			r := newReservation(gc)
   299  			r.lim = lim
   300  			r.burst = n
   301  			r.tokens = n
   302  			return r
   303  		}
   304  	}
   305  
   306  	// tokens are not enough
   307  
   308  	// Decide result
   309  	var expired bool
   310  	if deadline, has := ctx.Deadline(); has && deadline.Before(time.Now()) {
   311  		expired = true
   312  	}
   313  
   314  	addToListener := n <= lim.burst && !expired && wait
   315  
   316  	// Prepare reservation
   317  	r := newReservation(gc)
   318  	r.lim = lim
   319  	r.burst = n
   320  	if addToListener {
   321  		r.tokensGot, r.notifyTokensReady = context.WithCancel(context.WithValue(ctx, expectTokensKey, r.reservation))
   322  		lim.tokensChangedListeners = append(lim.tokensChangedListeners, r.tokensGot)
   323  	}
   324  
   325  	return r
   326  }
   327  
   328  func (lim *BurstLimiter) trackReservationRemove(r *reservation) {
   329  	lim.mu.Lock()
   330  	defer lim.mu.Unlock()
   331  
   332  	for i, tokensGot := range lim.tokensChangedListeners {
   333  		if r == tokensGot.Value(expectTokensKey).(*reservation) {
   334  			if i == len(lim.tokensChangedListeners)-1 {
   335  				lim.tokensChangedListeners = lim.tokensChangedListeners[:i]
   336  				return
   337  			}
   338  			lim.tokensChangedListeners = append(lim.tokensChangedListeners[:i], lim.tokensChangedListeners[i+1:]...)
   339  			return
   340  		}
   341  	}
   342  	return
   343  }