-
Notifications
You must be signed in to change notification settings - Fork 23
/
waiter.go
155 lines (135 loc) · 3.53 KB
/
waiter.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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
package irtt
import (
"fmt"
"strconv"
"strings"
"time"
)
// Waiter is implemented to return a wait time for final replies. See the
// documentation for Recorder for information on locking for concurrent access.
type Waiter interface {
// Wait returns the wait duration.
Wait(r *Recorder) time.Duration
String() string
}
// WaitDuration waits for a specific period of time.
type WaitDuration struct {
D time.Duration `json:"d"`
}
// Wait returns the wait duration.
func (w *WaitDuration) Wait(r *Recorder) time.Duration {
return w.D
}
func (w *WaitDuration) String() string {
return w.D.String()
}
// WaitMaxRTT waits for a factor of the maximum RTT
type WaitMaxRTT struct {
D time.Duration `json:"d"`
Factor int `json:"factor"`
}
// Wait returns the wait duration.
func (w *WaitMaxRTT) Wait(r *Recorder) time.Duration {
r.RLock()
defer r.RUnlock()
if r.RTTStats.N == 0 {
return w.D
}
return time.Duration(w.Factor) * r.RTTStats.Max
}
func (w *WaitMaxRTT) String() string {
return fmt.Sprintf("%dx%s", w.Factor, w.D)
}
// WaitMeanRTT waits for a factor of the mean RTT.
type WaitMeanRTT struct {
D time.Duration `json:"d"`
Factor int `json:"factor"`
}
// Wait returns the wait duration.
func (w *WaitMeanRTT) Wait(r *Recorder) time.Duration {
r.RLock()
defer r.RUnlock()
if r.RTTStats.N == 0 {
return w.D
}
return time.Duration(w.Factor) * r.RTTStats.Mean()
}
func (w *WaitMeanRTT) String() string {
return fmt.Sprintf("%dr%s", w.Factor, w.D)
}
// WaiterFactories are the registered Waiter factories.
var WaiterFactories = make([]WaiterFactory, 0)
// WaiterFactory can create a Waiter from a string.
type WaiterFactory struct {
FactoryFunc func(string) (Waiter, error)
Usage string
}
// RegisterWaiter registers a new Waiter.
func RegisterWaiter(fn func(string) (Waiter, error), usage string) {
WaiterFactories = append(WaiterFactories, WaiterFactory{fn, usage})
}
// NewWaiter returns a Waiter from a string.
func NewWaiter(s string) (Waiter, error) {
for _, fac := range WaiterFactories {
t, err := fac.FactoryFunc(s)
if err != nil {
return nil, err
}
if t != nil {
return t, nil
}
}
return nil, Errorf(NoSuchWaiter, "no such Waiter %s", s)
}
func init() {
RegisterWaiter(
func(s string) (t Waiter, err error) {
i := strings.Index(s, "x")
if i != -1 {
f, d, err := parseWait(s[:i], s[i+1:])
if err != nil {
return nil, Errorf(InvalidWaitString, "invalid wait %s (%s)", s, err)
}
return &WaitMaxRTT{D: d, Factor: f}, nil
}
return nil, nil
},
"#xduration: # times max RTT, or duration if no response",
)
RegisterWaiter(
func(s string) (t Waiter, err error) {
i := strings.Index(s, "r")
if i != -1 {
f, d, err := parseWait(s[:i], s[i+1:])
if err != nil {
return nil, Errorf(InvalidWaitString, "invalid wait %s (%s)", s, err)
}
return &WaitMeanRTT{D: d, Factor: f}, nil
}
return nil, nil
},
"#rduration: # times RTT, or duration if no response",
)
RegisterWaiter(
func(s string) (Waiter, error) {
if d, err := time.ParseDuration(s); err == nil {
return &WaitDuration{D: d}, nil
}
return nil, nil
},
"duration: fixed duration (see Duration units below)",
)
}
func parseWait(fstr string, dstr string) (factor int, dur time.Duration, err error) {
factor, err = strconv.Atoi(fstr)
if err != nil {
err = Errorf(InvalidWaitFactor, "unparseable factor %s", fstr)
return
}
dur, err = time.ParseDuration(dstr)
if err != nil {
err = Errorf(InvalidWaitDuration, "not a duration %s", dstr)
return
}
return
}