|
|
@@ -12,40 +12,43 @@ import (
|
|
|
"tailscale.com/net/tcpinfo"
|
|
|
)
|
|
|
|
|
|
-func (c *sclient) statsLoop(ctx context.Context) error {
|
|
|
+func (c *sclient) startStatsLoop(ctx context.Context) {
|
|
|
// Get the RTT initially to verify it's supported.
|
|
|
conn := c.tcpConn()
|
|
|
if conn == nil {
|
|
|
c.s.tcpRtt.Add("non-tcp", 1)
|
|
|
- return nil
|
|
|
+ return
|
|
|
}
|
|
|
if _, err := tcpinfo.RTT(conn); err != nil {
|
|
|
c.logf("error fetching initial RTT: %v", err)
|
|
|
c.s.tcpRtt.Add("error", 1)
|
|
|
- return nil
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
const statsInterval = 10 * time.Second
|
|
|
|
|
|
- ticker, tickerChannel := c.s.clock.NewTicker(statsInterval)
|
|
|
- defer ticker.Stop()
|
|
|
+ // Don't launch a goroutine; use a timer instead.
|
|
|
+ var gatherStats func()
|
|
|
+ gatherStats = func() {
|
|
|
+ // Do nothing if the context is finished.
|
|
|
+ if ctx.Err() != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
|
|
|
-statsLoop:
|
|
|
- for {
|
|
|
- select {
|
|
|
- case <-tickerChannel:
|
|
|
- rtt, err := tcpinfo.RTT(conn)
|
|
|
- if err != nil {
|
|
|
- continue statsLoop
|
|
|
- }
|
|
|
+ // Reschedule ourselves when this stats gathering is finished.
|
|
|
+ defer c.s.clock.AfterFunc(statsInterval, gatherStats)
|
|
|
|
|
|
- // TODO(andrew): more metrics?
|
|
|
+ // Gather TCP RTT information.
|
|
|
+ rtt, err := tcpinfo.RTT(conn)
|
|
|
+ if err == nil {
|
|
|
c.s.tcpRtt.Add(durationToLabel(rtt), 1)
|
|
|
-
|
|
|
- case <-ctx.Done():
|
|
|
- return ctx.Err()
|
|
|
}
|
|
|
+
|
|
|
+ // TODO(andrew): more metrics?
|
|
|
}
|
|
|
+
|
|
|
+ // Kick off the initial timer.
|
|
|
+ c.s.clock.AfterFunc(statsInterval, gatherStats)
|
|
|
}
|
|
|
|
|
|
// tcpConn attempts to get the underlying *net.TCPConn from this client's
|