github.com/status-im/status-go@v1.1.0/rtt/rtt.go (about) 1 package rtt 2 3 import ( 4 "context" 5 "sync" 6 "time" 7 8 errors "github.com/pkg/errors" 9 tcp "github.com/status-im/tcp-shaker" 10 ) 11 12 type Result struct { 13 Addr string 14 RTTMs int 15 Err error 16 } 17 18 // timeoutError indicates an error due to TCP connection timeout. 19 // tcp-shaker returns an error implementing this interface in such a case. 20 type timeoutError interface { 21 Timeout() bool 22 } 23 24 func runCheck(c *tcp.Checker, address string, timeout time.Duration) Result { 25 // mesaure RTT 26 start := time.Now() 27 // TCP Ping 28 err := c.CheckAddr(address, timeout) 29 // measure RTT 30 elapsed := time.Since(start) 31 latency := int(elapsed.Nanoseconds() / 1e6) 32 33 if err != nil { // don't confuse users with valid latency values on error 34 latency = -1 35 switch err.(type) { 36 case timeoutError: 37 err = errors.Wrap(err, "tcp check timeout") 38 case tcp.ErrConnect: 39 err = errors.Wrap(err, "unable to connect") 40 } 41 } 42 43 return Result{ 44 Addr: address, 45 RTTMs: latency, 46 Err: err, 47 } 48 } 49 50 func waitForResults(errCh <-chan error, resCh <-chan Result) (results []Result, err error) { 51 for { 52 select { 53 case err = <-errCh: 54 return nil, err 55 case res, ok := <-resCh: 56 if !ok { 57 return 58 } 59 results = append(results, res) 60 } 61 } 62 } 63 64 func CheckHosts(addresses []string, timeout time.Duration) ([]Result, error) { 65 c := tcp.NewChecker() 66 67 // channel for receiving possible checking loop failure 68 errCh := make(chan error, 1) 69 70 // stop the checking loop when function exists 71 ctx, stopChecker := context.WithCancel(context.Background()) 72 defer stopChecker() 73 74 // loop that queries Epoll and pipes events to CheckAddr() calls 75 go func() { 76 errCh <- c.CheckingLoop(ctx) 77 }() 78 // wait for CheckingLoop to prepare the epoll/kqueue 79 <-c.WaitReady() 80 81 // channel for returning results from concurrent checks 82 resCh := make(chan Result, len(addresses)) 83 84 var wg sync.WaitGroup 85 for i := 0; i < len(addresses); i++ { 86 wg.Add(1) 87 go func(address string, resCh chan<- Result) { 88 defer wg.Done() 89 resCh <- runCheck(c, address, timeout) 90 }(addresses[i], resCh) 91 } 92 // wait for all the routines to finish before closing results channel 93 wg.Wait() 94 close(resCh) 95 96 // wait for the results for all addresses or a checking loop error 97 return waitForResults(errCh, resCh) 98 }