status-go/rtt/rtt.go
frank 38308d48f2
feat_: log on panic (#5849)
* feat_: log error and stacktrace when panic in goroutine

* test_: add test TestSafeGo

* chore_: rename logAndCall to call

* chore_: rename SafeGo to Go

* chore_: make lint-fix

* chore_: use t.Cleanup

* chore_: Revert "chore_: use t.Cleanup"

This reverts commit 4eb420d179cc0e208e84c13cb941e6b3d1ed9819.

* chore_: Revert "chore_: make lint-fix"

This reverts commit fcc995f157e671a4229b47419c3a0e4004b5fdab.

* chore_: Revert "chore_: rename SafeGo to Go"

This reverts commit a6d73d6df583f313032d79aac62f66328039cb55.

* chore_: Revert "chore_: rename logAndCall to call"

This reverts commit 8fbe993bedb9fbba67349a44f151e2dd5e3bc4cc.

* chore_: Revert "test_: add test TestSafeGo"

This reverts commit a1fa91839f3960398980c6bf456e6462ec944819.

* chore_: Revert "feat_: log error and stacktrace when panic in goroutine"

This reverts commit f612dd828fa2ce410d0e806fe773ecbe3e86a68a.

* feat_: log error and stacktrace when panic in goroutine

* chore_: make lint-fix

* chore_: rename logAndCall to call

* chore_: renaming LogOnPanic

* chore_: update rest goroutine function calls

* chore_: make lint-fix
2024-09-27 06:37:32 +08:00

103 lines
2.3 KiB
Go

package rtt
import (
"context"
"sync"
"time"
errors "github.com/pkg/errors"
tcp "github.com/status-im/tcp-shaker"
gocommon "github.com/status-im/status-go/common"
)
type Result struct {
Addr string
RTTMs int
Err error
}
// timeoutError indicates an error due to TCP connection timeout.
// tcp-shaker returns an error implementing this interface in such a case.
type timeoutError interface {
Timeout() bool
}
func runCheck(c *tcp.Checker, address string, timeout time.Duration) Result {
// mesaure RTT
start := time.Now()
// TCP Ping
err := c.CheckAddr(address, timeout)
// measure RTT
elapsed := time.Since(start)
latency := int(elapsed.Nanoseconds() / 1e6)
if err != nil { // don't confuse users with valid latency values on error
latency = -1
switch err.(type) {
case timeoutError:
err = errors.Wrap(err, "tcp check timeout")
case tcp.ErrConnect:
err = errors.Wrap(err, "unable to connect")
}
}
return Result{
Addr: address,
RTTMs: latency,
Err: err,
}
}
func waitForResults(errCh <-chan error, resCh <-chan Result) (results []Result, err error) {
for {
select {
case err = <-errCh:
return nil, err
case res, ok := <-resCh:
if !ok {
return
}
results = append(results, res)
}
}
}
func CheckHosts(addresses []string, timeout time.Duration) ([]Result, error) {
c := tcp.NewChecker()
// channel for receiving possible checking loop failure
errCh := make(chan error, 1)
// stop the checking loop when function exists
ctx, stopChecker := context.WithCancel(context.Background())
defer stopChecker()
// loop that queries Epoll and pipes events to CheckAddr() calls
go func() {
defer gocommon.LogOnPanic()
errCh <- c.CheckingLoop(ctx)
}()
// wait for CheckingLoop to prepare the epoll/kqueue
<-c.WaitReady()
// channel for returning results from concurrent checks
resCh := make(chan Result, len(addresses))
var wg sync.WaitGroup
for i := 0; i < len(addresses); i++ {
wg.Add(1)
go func(address string, resCh chan<- Result) {
defer gocommon.LogOnPanic()
defer wg.Done()
resCh <- runCheck(c, address, timeout)
}(addresses[i], resCh)
}
// wait for all the routines to finish before closing results channel
wg.Wait()
close(resCh)
// wait for the results for all addresses or a checking loop error
return waitForResults(errCh, resCh)
}