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 }