-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlimiter.go
More file actions
59 lines (52 loc) · 1.5 KB
/
limiter.go
File metadata and controls
59 lines (52 loc) · 1.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
package limit
import (
"context"
"golang.org/x/sync/semaphore"
"time"
)
// Limiter provides a way to bound too many concurrent tasks to run. It is like a
// combination of sliding window and token bucket.
type Limiter struct {
sem *semaphore.Weighted
duration time.Duration
}
// WaitN waits for n tokens to continue, blocking until resources are available
// or ctx is done. On success, returns nil. On failure, returns ctx.Err() and
// leaves the limiter unchanged. If ctx is already done, WaitN may still succeed
// without blocking.
func (l *Limiter) WaitN(ctx context.Context, n int64) error {
var err error
if err = l.sem.Acquire(ctx, n); err == nil {
time.AfterFunc(l.duration, func() {
l.sem.Release(n)
})
}
return err
}
// Wait waits for 1 token to continue.
func (l *Limiter) Wait(ctx context.Context) error {
return l.WaitN(ctx, 1)
}
// PassN acquires n tokens to continue without blocking. On success, returns
// true. On failure, returns false and leaves the limiter unchanged.
func (l *Limiter) PassN(n int64) bool {
var success bool
if success = l.sem.TryAcquire(n); success {
time.AfterFunc(l.duration, func() {
l.sem.Release(n)
})
}
return success
}
// Pass acquires 1 token to continue without blocking.
func (l *Limiter) Pass() bool {
return l.PassN(1)
}
// New returns a new limiter which ensures that at most n tasks can run in the
// time window of d.
func New(n int64, d time.Duration) *Limiter {
return &Limiter{
sem: semaphore.NewWeighted(n),
duration: d,
}
}