mirror of
https://github.com/netbirdio/netbird.git
synced 2026-08-28 18:41:30 +02:00
Losing the last network only flipped the availability state: the dead management, signal and relay sockets stayed silently connected until their own timeouts, so the client kept reporting Connected with no network at all. Introduce client/netevents with a Manager that ties the availability state, the connection sweeper and the status recorder together, and move the netstate and netsweep packages under it (netsweep renamed to sweep). SetNetworkAvailable(false) now also sweeps the registered connections so their owners redial and the listener reaches the NoNetwork state. The Android and iOS bindings own a Manager instance and inject it through the constructors; consumers hold the concrete *Manager whose nil zero value reports always-online and never sweeps, with interfaces kept only as parameter contracts. The relay guard settle wait moved into the Manager as WaitSettled, removing the netevents import from the relay package.
57 lines
1.2 KiB
Go
57 lines
1.2 KiB
Go
package grpc
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
|
|
"github.com/cenkalti/backoff/v4"
|
|
)
|
|
|
|
// ChangeWatcher exposes OS network availability transitions.
|
|
type ChangeWatcher interface {
|
|
Changed() <-chan struct{}
|
|
}
|
|
|
|
// Retry mirrors backoff.Retry, but the sleep between attempts also wakes on
|
|
// OS network availability transitions: an operation cut down by a network
|
|
// change retries the moment the network settles instead of sleeping through
|
|
// the recovery. A nil watcher never fires, leaving plain backoff.Retry
|
|
// behavior.
|
|
func Retry(ctx context.Context, operation backoff.Operation, bo backoff.BackOff, watcher ChangeWatcher) error {
|
|
bo.Reset()
|
|
for {
|
|
err := operation()
|
|
if err == nil {
|
|
return nil
|
|
}
|
|
|
|
var permanent *backoff.PermanentError
|
|
if errors.As(err, &permanent) {
|
|
return permanent.Err
|
|
}
|
|
|
|
next := bo.NextBackOff()
|
|
if next == backoff.Stop {
|
|
if cerr := ctx.Err(); cerr != nil {
|
|
return cerr
|
|
}
|
|
return err
|
|
}
|
|
|
|
var changed <-chan struct{}
|
|
if watcher != nil {
|
|
changed = watcher.Changed()
|
|
}
|
|
timer := time.NewTimer(next)
|
|
select {
|
|
case <-timer.C:
|
|
case <-changed:
|
|
timer.Stop()
|
|
case <-ctx.Done():
|
|
timer.Stop()
|
|
return ctx.Err()
|
|
}
|
|
}
|
|
}
|