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 }