-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlimiter.go
More file actions
73 lines (66 loc) · 1.82 KB
/
limiter.go
File metadata and controls
73 lines (66 loc) · 1.82 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
60
61
62
63
64
65
66
67
68
69
70
71
72
73
package bwlimit
import (
"net"
)
var DefaultNetDialer = &net.Dialer{}
type Limiter struct {
*Ticker
Reads *Operation
Writes *Operation
}
// NewLimiter returns a new limiter from DefaultTicker.
// If DefaultTicker has been stopped, returns nil.
// If you provide limits, the first will set
// both read and write limits, the second will set the write limit.
// Limits are applied in 100ms slices with fractional carry-over between
// slices, so very low rates are accurate over time but can be bursty
// at slice boundaries.
//
// To stop the Limiter and free it's resources, call Stop.
func NewLimiter(limits ...int64) *Limiter {
return DefaultTicker.NewLimiter(limits...)
}
// Stop stops the Limiter and frees any resources. Reads and writes on
// a stopped and rate-limited Limiter returns io.EOF. On an unlimited
// Limiter they function as normal.
//
// Count and Rate metrics are not updated after Stop.
func (l *Limiter) Stop() {
l.Reads.Stop()
l.Writes.Stop()
}
// alreadyLimits returns true if cd is already limited by this Limiter.
// This lets us help the user avoiding double-accounting bandwidth.
func (l *Limiter) alreadyLimits(cd ContextDialer) bool {
seen := make(map[*Dialer]struct{})
for {
if d, ok := cd.(*Dialer); ok {
if d == nil {
return false
}
if d.Limiter == l {
return true
}
if _, ok := seen[d]; ok {
return false
}
seen[d] = struct{}{}
cd = d.ContextDialer
} else {
return false
}
}
}
// Wrap returns a ContextDialer wrapping cd that is bandwidth limited by this Limiter.
//
// If cd is nil we use DefaultNetDialer. If cd is already limited by this Limiter, cd
// is returned unchanged.
func (l *Limiter) Wrap(cd ContextDialer) ContextDialer {
if cd == nil {
cd = DefaultNetDialer
}
if l.alreadyLimits(cd) {
return cd
}
return &Dialer{ContextDialer: cd, Limiter: l}
}