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 }