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

     1  // Copyright 2023 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  
     7  import (
     8  	"context"
     9  	"fmt"
    10  	"runtime"
    11  	"time"
    12  )
    13  
    14  // A Reservation holds information about events that are permitted by a BurstLimiter to happen after a delay.
    15  // A Reservation may be canceled, which may enable the BurstLimiter to permit additional events.
    16  type Reservation struct {
    17  	*reservation
    18  }
    19  
    20  // reservation is the real representation of *Reservation.
    21  // The extra level of indirection ensures that a Reservation
    22  // with a finalizer, that cycle in cyclic structure is not
    23  // guaranteed to be garbage collected
    24  // https://tip.golang.org/doc/gc-guide#Where_Go_Values_Live
    25  type reservation struct {
    26  	ok  bool
    27  	lim *BurstLimiter
    28  
    29  	// [0, tokens, burst]
    30  	burst  int // reservation bucket size
    31  	tokens int // tokens got(reserved) from BurstLimiter, Cancel(put back) must be called to the BurstLimiter after Wait
    32  
    33  	// timeToAct time.Time       // now + wait, wait if bucket is not enough
    34  	tokensGot         context.Context // chan to notify tokens is put, check if enough
    35  	notifyTokensReady context.CancelFunc
    36  
    37  	// test only
    38  	canceled context.CancelFunc
    39  }
    40  
    41  func newReservation(gc bool) *Reservation {
    42  	r := &Reservation{reservation: &reservation{}}
    43  	if gc {
    44  		runtime.SetFinalizer(r, (*Reservation).Cancel)
    45  	}
    46  	return r
    47  }
    48  
    49  func (r *Reservation) removeGC() *Reservation {
    50  	// no need for a finalizer anymore
    51  	runtime.SetFinalizer(r, nil)
    52  	return r
    53  }
    54  
    55  // OK returns whether the limiter can provide the requested number of tokens
    56  // within the maximum wait time. If OK is false, Delay returns InfDuration, and
    57  // Cancel does nothing.
    58  func (r *Reservation) OK() bool {
    59  	return r.burst <= r.lim.Burst()
    60  }
    61  
    62  // Ready returns whether the limiter can provide the requested number of tokens
    63  // within the maximum wait time. If Ready is false, Wait returns nil directly, and
    64  // Cancel or GC does put back the token reserved in the Reservation.
    65  // If Ready is false, WaitN blocks until lim permits n events to happen.
    66  func (r *Reservation) Ready() bool {
    67  	return r.tokens >= r.burst
    68  }
    69  
    70  // Wait blocks before taking the reserved action
    71  // Wait 当没有可用或足够的事件时,将阻塞等待
    72  func (r *Reservation) Wait(ctx context.Context) error {
    73  	if r.burst <= 0 {
    74  		r.burst = 0
    75  		r.tokens = r.burst
    76  		return nil
    77  	}
    78  
    79  	if r.Ready() {
    80  		return nil
    81  	}
    82  
    83  	var burst = r.lim.Burst()
    84  	if r.burst > burst {
    85  		return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", r.burst, burst)
    86  	}
    87  	timer := time.NewTimer(starvationThresholdNs)
    88  	defer timer.Stop()
    89  	for {
    90  		// fast path
    91  		if r.tokensGot == nil {
    92  			// We can proceed.
    93  			if r.lim.GetTokenN(r.burst - r.tokens) {
    94  				r.tokens = r.burst
    95  				return nil
    96  			}
    97  			// Wait if necessary
    98  			if !timer.Stop() {
    99  				<-timer.C
   100  			}
   101  			timer.Reset(starvationThresholdNs)
   102  
   103  			select {
   104  			case <-ctx.Done():
   105  				// Context was canceled before we could proceed.  Cancel the
   106  				// reservation, which may permit other events to proceed sooner.
   107  				r.Cancel()
   108  				return ctx.Err()
   109  			case <-timer.C:
   110  				break
   111  			}
   112  			continue
   113  		}
   114  
   115  		// Wait if necessary
   116  		select {
   117  		case <-r.tokensGot.Done():
   118  			// We can proceed.
   119  			return nil
   120  		case <-ctx.Done():
   121  			// Context was canceled before we could proceed.  Cancel the
   122  			// reservation, which may permit other events to proceed sooner.
   123  			r.Cancel()
   124  			return ctx.Err()
   125  		}
   126  	}
   127  }
   128  
   129  // Cancel indicates that the reservation holder will not perform the reserved action
   130  // and reverses the effects of this Reservation on the rate limit as much as possible,
   131  // considering that other reservations may have already been made.
   132  func (r *Reservation) Cancel() {
   133  	defer func() {
   134  		// no need for a finalizer anymore
   135  		runtime.SetFinalizer(r, nil)
   136  	}()
   137  	if r.canceled != nil {
   138  		r.canceled()
   139  	}
   140  	if r.burst <= 0 {
   141  		return
   142  	}
   143  	defer func() { r.burst = 0 }() // set Reservation as empty
   144  	r.lim.trackReservationRemove(r.reservation)
   145  	r.lim.PutTokenN(r.tokens)
   146  	r.tokens = 0
   147  	return
   148  }
   149  
   150  // PutToken (as Complete): refill all tokens taken by the Reservation back to BurstLimiter.
   151  // PutToken is shorthand for Cancel().
   152  func (r *Reservation) PutToken() {
   153  	r.Cancel()
   154  }