github.com/searKing/golang/go@v1.2.117/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 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 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  // NewReorderBuffer returns a new BurstLimiter with exactly only one token that allows
   103  // instructions to be committed in-order.
   104  // - Allocated by `Reserve` into account when allowing future events
   105  // - Wait by `Wait` blocks until lim permits n events to happen
   106  // - Allow and Wait Complete by `PutToken`
   107  // - Reserve Complete by `Cancel` of the Reservation self, GC Cancel supported
   108  // See https://en.wikipedia.org/wiki/Re-order_buffer for more about Reorder buffer.
   109  // See https://web.archive.org/web/20040724215416/http://lgjohn.okstate.edu/6253/lectures/reorder.pdf for more about Reorder buffer.
   110  func NewReorderBuffer() *BurstLimiter {
   111  	return NewFullBurstLimiter(1)
   112  }
   113  
   114  // SetBurst sets a new burst size for the limiter.
   115  func (lim *BurstLimiter) SetBurst(newBurst int) {
   116  	lim.mu.Lock()
   117  	defer lim.mu.Unlock()
   118  	lim.burst = newBurst
   119  }
   120  
   121  // Allow is shorthand for AllowN(time.Now(), 1).
   122  // 当没有可用或足够的事件时,返回false
   123  func (lim *BurstLimiter) Allow() bool {
   124  	return lim.AllowN(1)
   125  }
   126  
   127  // AllowN reports whether n events may happen at time now.
   128  // AllowN is shorthand for GetTokenN.
   129  // Use this method if you intend to drop / skip events that exceed the rate limit.
   130  // Otherwise, use Reserve or Wait.
   131  // 当没有可用或足够的事件时,返回false
   132  func (lim *BurstLimiter) AllowN(n int) bool {
   133  	return lim.GetTokenN(n)
   134  }
   135  
   136  // Reserve is shorthand for ReserveN(1).
   137  // 当没有可用或足够的事件时,返回 Reservation,和要等待多久才能获得足够的事件。
   138  func (lim *BurstLimiter) Reserve(ctx context.Context) *Reservation {
   139  	return lim.ReserveN(ctx, 1)
   140  }
   141  
   142  // ReserveN returns a Reservation that indicates how long the caller must wait before n events happen.
   143  // The BurstLimiter takes this Reservation into account when allowing future events.
   144  // ReserveN returns false if n exceeds the BurstLimiter's burst size.
   145  // Usage example:
   146  //
   147  //	    // Allocate: The dispatch stage reserves space in the reorder buffer for instructions in program order.
   148  //		r := lim.ReserveN(context.Background(), 1)
   149  //		if !r.OK() {
   150  //			// Not allowed to act! Did you remember to set lim.burst to be > 0 ?
   151  //			return
   152  //		}
   153  //
   154  //		// Execute: out-of-order execution
   155  //		Act()
   156  //
   157  //		// Wait: The complete stage must wait for instructions to finish execution.
   158  //		if err:= r.Wait(); err!=nil {
   159  //		// Not allowed to act! Reservation or context canceled ?
   160  //			return
   161  //		}
   162  //		// Complete: Finished instructions are allowed to write results in order into the architected registers.
   163  //		// It allows instructions to be committed in-order.
   164  //		defer r.PutToken()
   165  //
   166  //		// Execute: in-order execution
   167  //		Act()
   168  //
   169  // Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events.
   170  // If you need to respect a deadline or cancel the delay, use Wait instead.
   171  // To drop or skip events exceeding rate limit, use Allow instead.
   172  // 当没有可用或足够的事件时,返回 Reservation,和要等待多久才能获得足够的事件。
   173  // See https://en.wikipedia.org/wiki/Re-order_buffer for more about Reorder buffer.
   174  // See https://web.archive.org/web/20040724215416/http://lgjohn.okstate.edu/6253/lectures/reorder.pdf for more about Reorder buffer.
   175  func (lim *BurstLimiter) ReserveN(ctx context.Context, n int) *Reservation {
   176  	r := lim.reserveN(ctx, n, true, true)
   177  	return r
   178  }
   179  
   180  // Wait is shorthand for WaitN(ctx, 1).
   181  func (lim *BurstLimiter) Wait(ctx context.Context) (err error) {
   182  	return lim.WaitN(ctx, 1)
   183  }
   184  
   185  // WaitN blocks until lim permits n events to happen.
   186  // It returns an error if n exceeds the BurstLimiter's burst size, the Context is
   187  // canceled, or the expected wait time exceeds the Context's Deadline.
   188  // The burst limit is ignored if the rate limit is Inf.
   189  func (lim *BurstLimiter) WaitN(ctx context.Context, n int) (err error) {
   190  	lim.mu.Lock()
   191  	burst := lim.burst
   192  	lim.mu.Unlock()
   193  
   194  	if n > burst {
   195  		return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, burst)
   196  	}
   197  	// Check if ctx is already cancelled
   198  	select {
   199  	case <-ctx.Done():
   200  		return ctx.Err()
   201  	default:
   202  	}
   203  	// Reserve
   204  	r := lim.reserveN(ctx, n, true, false)
   205  	if r.Ready() { // tokens already hold by the Reservation
   206  		return nil
   207  	}
   208  
   209  	// Wait if necessary
   210  	return r.Wait(ctx)
   211  }
   212  
   213  // PutToken is shorthand for PutTokenN(ctx, 1).
   214  func (lim *BurstLimiter) PutToken() {
   215  	lim.PutTokenN(1)
   216  }
   217  
   218  func (lim *BurstLimiter) PutTokenN(n int) {
   219  	lim.mu.Lock()
   220  	defer lim.mu.Unlock()
   221  	lim.tokens += n
   222  	// drop if overflowed
   223  	if lim.tokens > lim.burst {
   224  		lim.tokens = lim.burst
   225  	}
   226  
   227  	for i := 0; i < len(lim.tokensChangedListeners); i++ {
   228  		tokensGot := lim.tokensChangedListeners[i]
   229  		r := tokensGot.Value(expectTokensKey).(*reservation)
   230  		if r.burst <= 0 {
   231  			// remove notified
   232  			if i == len(lim.tokensChangedListeners)-1 {
   233  				lim.tokensChangedListeners = lim.tokensChangedListeners[:i]
   234  			} else {
   235  				lim.tokensChangedListeners = append(lim.tokensChangedListeners[:i], lim.tokensChangedListeners[i+1:]...)
   236  			}
   237  			r.notifyTokensReady()
   238  			continue
   239  		}
   240  
   241  		tokensWait := r.burst - r.tokens
   242  
   243  		// tokens in the Bucket is not enough for the Reservation
   244  		if lim.tokens < tokensWait {
   245  			r.tokens += lim.tokens
   246  			lim.tokens = 0
   247  			break
   248  		}
   249  
   250  		// enough
   251  		r.tokens = r.burst
   252  		lim.tokens -= tokensWait
   253  		// remove notified
   254  		if i == len(lim.tokensChangedListeners)-1 {
   255  			lim.tokensChangedListeners = lim.tokensChangedListeners[:i]
   256  		} else {
   257  			lim.tokensChangedListeners = append(lim.tokensChangedListeners[:i], lim.tokensChangedListeners[i+1:]...)
   258  		}
   259  		r.notifyTokensReady()
   260  		continue
   261  	}
   262  }
   263  
   264  // GetToken is shorthand for GetTokenN(ctx, 1).
   265  func (lim *BurstLimiter) GetToken() (ok bool) {
   266  	return lim.GetTokenN(1)
   267  }
   268  
   269  // GetTokenN returns true if token is got
   270  func (lim *BurstLimiter) GetTokenN(n int) (ok bool) {
   271  	lim.mu.Lock()
   272  	defer lim.mu.Unlock()
   273  	return lim.getTokenNLocked(n)
   274  }
   275  
   276  // getTokenNLocked returns true if token is got
   277  // advance calculates and returns an updated state for lim resulting from the passage of time.
   278  // lim is not changed.
   279  // getTokenNLocked requires that lim.mu is held.
   280  func (lim *BurstLimiter) getTokenNLocked(n int) (ok bool) {
   281  	if n <= 0 {
   282  		return true
   283  	}
   284  	if lim.tokens >= n {
   285  		lim.tokens -= n
   286  		return true
   287  	}
   288  	return false
   289  }
   290  
   291  // reserveN is a helper method for AllowN, ReserveN, and WaitN.
   292  // maxFutureReserve specifies the maximum reservation wait duration allowed.
   293  // reserveN returns Reservation, not *reservation, to avoid allocation in AllowN and WaitN.
   294  func (lim *BurstLimiter) reserveN(ctx context.Context, n int, wait bool, gc bool) *Reservation {
   295  	if n <= 0 {
   296  		r := newReservation(gc)
   297  		r.lim = lim
   298  		r.burst = 0
   299  		r.tokens = 0
   300  		return r
   301  	}
   302  
   303  	lim.mu.Lock()
   304  	defer lim.mu.Unlock()
   305  
   306  	// tokens are enough
   307  	if lim.tokens >= n && len(lim.tokensChangedListeners) == 0 {
   308  		// get n tokens from lim
   309  		if lim.getTokenNLocked(n) {
   310  			r := newReservation(gc)
   311  			r.lim = lim
   312  			r.burst = n
   313  			r.tokens = n
   314  			return r
   315  		}
   316  	}
   317  
   318  	// tokens are not enough
   319  
   320  	// Decide result
   321  	var expired bool
   322  	if deadline, has := ctx.Deadline(); has && deadline.Before(time.Now()) {
   323  		expired = true
   324  	}
   325  
   326  	addToListener := n <= lim.burst && !expired && wait
   327  
   328  	// Prepare reservation
   329  	r := newReservation(gc)
   330  	r.lim = lim
   331  	r.burst = n
   332  	if addToListener {
   333  		r.tokensGot, r.notifyTokensReady = context.WithCancel(context.WithValue(ctx, expectTokensKey, r.reservation))
   334  		lim.tokensChangedListeners = append(lim.tokensChangedListeners, r.tokensGot)
   335  	}
   336  
   337  	return r
   338  }
   339  
   340  func (lim *BurstLimiter) trackReservationRemove(r *reservation) {
   341  	lim.mu.Lock()
   342  	defer lim.mu.Unlock()
   343  
   344  	for i, tokensGot := range lim.tokensChangedListeners {
   345  		if r == tokensGot.Value(expectTokensKey).(*reservation) {
   346  			if i == len(lim.tokensChangedListeners)-1 {
   347  				lim.tokensChangedListeners = lim.tokensChangedListeners[:i]
   348  				return
   349  			}
   350  			lim.tokensChangedListeners = append(lim.tokensChangedListeners[:i], lim.tokensChangedListeners[i+1:]...)
   351  			return
   352  		}
   353  	}
   354  	return
   355  }