@@ -11,6 +11,7 @@ import (
11
11
"sync"
12
12
"time"
13
13
14
+ "github.com/cenkalti/backoff/v4"
14
15
"github.com/google/uuid"
15
16
"go4.org/netipx"
16
17
"golang.org/x/xerrors"
@@ -424,24 +425,43 @@ func (c *Conn) Ping(ctx context.Context, ip netip.Addr) (time.Duration, error) {
424
425
// address is reachable. It's the callers responsibility to provide
425
426
// a timeout, otherwise this function will block forever.
426
427
func (c * Conn ) AwaitReachable (ctx context.Context , ip netip.Addr ) bool {
427
- ticker := time .NewTicker (time .Millisecond * 100 )
428
- defer ticker .Stop ()
429
- completedCtx , completed := context .WithCancel (ctx )
428
+ ctx , cancel := context .WithCancel (ctx )
429
+ defer cancel () // Cancel all pending pings on exit.
430
+
431
+ completedCtx , completed := context .WithCancel (context .Background ())
432
+ defer completed ()
433
+
430
434
run := func () {
431
- ctx , cancelFunc := context .WithTimeout (completedCtx , time .Second )
432
- defer cancelFunc ()
435
+ // Safety timeout, initially we'll have around 10-20 goroutines
436
+ // running in parallel. The exponential backoff will converge
437
+ // around ~1 ping / 30s, this means we'll have around 10-20
438
+ // goroutines pending towards the end as well.
439
+ ctx , cancel := context .WithTimeout (ctx , 5 * time .Minute )
440
+ defer cancel ()
441
+
433
442
_ , err := c .Ping (ctx , ip )
434
443
if err == nil {
435
444
completed ()
436
445
}
437
446
}
447
+
448
+ eb := backoff .NewExponentialBackOff ()
449
+ eb .MaxElapsedTime = 0
450
+ eb .InitialInterval = 50 * time .Millisecond
451
+ eb .MaxInterval = 30 * time .Second
452
+ // Consume the first interval since
453
+ // we'll fire off a ping immediately.
454
+ _ = eb .NextBackOff ()
455
+
456
+ t := backoff .NewTicker (eb )
457
+ defer t .Stop ()
458
+
438
459
go run ()
439
- defer completed ()
440
460
for {
441
461
select {
442
462
case <- completedCtx .Done ():
443
463
return true
444
- case <- ticker .C :
464
+ case <- t .C :
445
465
// Pings can take a while, so we can run multiple
446
466
// in parallel to return ASAP.
447
467
go run ()
0 commit comments