github.com/kaydxh/golang@v0.0.131/go/time/rate/rate.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package rate 23 24 import ( 25 "fmt" 26 "sync" 27 "time" 28 29 sync_ "github.com/kaydxh/golang/go/sync" 30 ) 31 32 type Limiter struct { 33 mu sync.Mutex 34 burst int 35 tokens int 36 cond *sync_.Cond 37 } 38 39 // Burst returns the maximum burst size. Burst is the maximum number of tokens 40 // that can be consumed in a single call to Allow, Reserve, or Wait, so higher 41 // Burst values allow more events to happen at once. 42 // A zero Burst allows no events, unless limit == Inf. 43 func (lim *Limiter) Burst() int { 44 lim.mu.Lock() 45 defer lim.mu.Unlock() 46 return lim.burst 47 } 48 49 // NewLimiter returns a new Limiter that allows events up to rate r and permits 50 // bursts of at most b tokens. 51 func NewLimiter(b int) *Limiter { 52 l := &Limiter{ 53 burst: b, 54 tokens: b, 55 } 56 57 l.cond = sync_.NewCond(&l.mu) 58 59 return l 60 } 61 62 func (lim *Limiter) Bursting() int { 63 lim.mu.Lock() 64 defer lim.mu.Unlock() 65 66 return lim.burst - lim.tokens 67 } 68 69 // Allow is shorthand for AllowN(time.Now(), 1). 70 func (lim *Limiter) Allow() bool { 71 return lim.AllowN(time.Now(), 1, 0) 72 } 73 74 // AllowWaitUntil is shorthand for AllowFor(-1). 75 func (lim *Limiter) AllowWaitUntil() bool { 76 return lim.AllowFor(-1 * time.Second) 77 } 78 79 // Allow is shorthand for AllowN(time.Now(), 1). 80 func (lim *Limiter) AllowFor(timeout time.Duration) bool { 81 return lim.AllowN(time.Now(), 1, timeout) 82 } 83 84 // AllowN reports whether n events may happen at time now. 85 // Use this method if you intend to drop / skip events that exceed the rate limit. 86 // Otherwise use Reserve or Wait. 87 func (lim *Limiter) AllowN(now time.Time, n int, timeout time.Duration) bool { 88 ok := lim.reserveN(now, n).ok 89 if ok { 90 return true 91 } 92 93 if timeout == 0 { 94 return false 95 } 96 97 err := lim.WaitFor(timeout) 98 if err != nil { 99 return false 100 } 101 102 return true 103 } 104 105 func (lim *Limiter) Put() { 106 lim.PutN(1) 107 } 108 109 func (lim *Limiter) PutN(n int) { 110 lim.mu.Lock() 111 defer lim.mu.Unlock() 112 113 lim.tokens += n 114 if lim.tokens > lim.burst { 115 lim.tokens = lim.burst 116 } 117 118 lim.cond.Signal() 119 } 120 121 // reserveN is a helper method for AllowN, ReserveN, and WaitN. 122 // maxFutureReserve specifies the maximum reservation wait duration allowed. 123 // reserveN returns Reservation, not *Reservation, to avoid allocation in AllowN and WaitN. 124 func (lim *Limiter) reserveN(now time.Time, n int) Reservation { 125 lim.mu.Lock() 126 defer lim.mu.Unlock() 127 128 if lim.tokens >= n { 129 lim.tokens -= n 130 return Reservation{ 131 ok: true, 132 lim: lim, 133 } 134 } 135 136 return Reservation{ 137 ok: false, 138 lim: lim, 139 } 140 } 141 142 // ReserveN returns a Reservation that indicates how long the caller must wait before n events happen. 143 // The Limiter takes this Reservation into account when allowing future events. 144 // The returned Reservation’s OK() method returns false if n exceeds the Limiter's burst size. 145 // Usage example: 146 // r := lim.ReserveN(time.Now(), 1) 147 // if !r.OK() { 148 // // Not allowed to act! Did you remember to set lim.burst to be > 0 ? 149 // return 150 // } 151 // err := lim.WaitFor(timeout) 152 // Act() 153 // Use this method if you wish to wait and slow down in accordance with the rate limit without dropping events. 154 // If you need to respect a deadline or cancel the delay, use Wait instead. 155 // To drop or skip events exceeding rate limit, use Allow instead. 156 157 // A Reservation holds information about events that are permitted by a Limiter to happen after a delay. 158 // A Reservation may be canceled, which may enable the Limiter to permit additional events. 159 // Wait is shorthand for WaitN(ctx, 1). 160 func (lim *Limiter) WaitFor(timeout time.Duration) (err error) { 161 return lim.WaitN(timeout, 1) 162 } 163 164 // WaitN blocks until lim permits n events to happen. 165 // It returns an error if n exceeds the Limiter's burst size, the Context is 166 // canceled, or the expected wait time exceeds the Context's Deadline. 167 // The burst limit is ignored if the rate limit is Inf. 168 func (lim *Limiter) WaitN(timeout time.Duration, n int) (err error) { 169 lim.mu.Lock() 170 burst := lim.burst 171 lim.mu.Unlock() 172 if n > burst { 173 return fmt.Errorf("rate: Wait(n=%d) exceeds limiter's burst %d", n, burst) 174 } 175 176 pred := func() bool { 177 return lim.tokens >= n 178 } 179 180 do := func() error { 181 lim.tokens -= n 182 return nil 183 } 184 185 if timeout >= 0 { 186 return lim.cond.WaitForDo(timeout, pred, do) 187 } else { 188 lim.cond.WaitUntilDo(pred, do) 189 return nil 190 } 191 } 192 193 type Reservation struct { 194 ok bool 195 lim *Limiter 196 tokens int 197 }