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