/
bandwidth.go
88 lines (72 loc) · 1.76 KB
/
bandwidth.go
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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
package quicwrapper
import (
"math"
"sync"
"time"
"github.com/getlantern/ema"
"github.com/getlantern/ops"
quic "github.com/getlantern/quic-go"
)
type Bandwidth = quic.Bandwidth
const (
Mib = 1024 * 1024 // 1 Mebibit Bandwidth
)
type BandwidthEstimator interface {
BandwidthEstimate() Bandwidth
}
const (
EMABandwidthSamplerDefaultPeriod = 1 * time.Second
EMABandwidthSamplerDefaultWindow = 15 * time.Second
)
// Samples and averages bandwidth estimates from another
// BandwidthEstimator
type EMABandwidthSampler struct {
estimate *ema.EMA
source BandwidthEstimator
period time.Duration
window time.Duration
done chan struct{}
start sync.Once
stop sync.Once
}
func NewEMABandwidthSampler(from BandwidthEstimator) *EMABandwidthSampler {
return NewEMABandwidthSamplerParams(from, EMABandwidthSamplerDefaultPeriod, EMABandwidthSamplerDefaultWindow)
}
func NewEMABandwidthSamplerParams(from BandwidthEstimator, period time.Duration, window time.Duration) *EMABandwidthSampler {
alpha := 1.0 - math.Exp(-float64(period)/float64(window))
return &EMABandwidthSampler{
estimate: ema.New(0, alpha),
source: from,
period: period,
done: make(chan struct{}),
}
}
func (bs *EMABandwidthSampler) BandwidthEstimate() Bandwidth {
return Bandwidth(bs.estimate.Get())
}
func (bs *EMABandwidthSampler) Start() {
bs.start.Do(func() {
ops.Go(func() {
for {
select {
case <-bs.done:
return
case <-time.After(bs.period):
bs.update()
}
}
})
})
}
func (bs *EMABandwidthSampler) Stop() {
bs.stop.Do(func() {
close(bs.done)
})
}
func (bs *EMABandwidthSampler) Clear() {
bs.estimate.Clear()
}
func (bs *EMABandwidthSampler) update() {
sample := bs.source.BandwidthEstimate()
bs.estimate.Update(float64(sample))
}