Files
ios-client/NetBirdTVNetworkExtension
Zoltan Papp e8dbf9c2fa Fire the adapter's stop completion only after the Go run loop has exited (#223)
* Fire the adapter's stop completion only after the Go run loop has exited

The restart path used the connection listener's onDisconnected callback as
the "stop completed" signal and started the next engine from inside it. The
Go notifier delivers that callback synchronously from the outgoing Run(),
before finishRun() releases the run slot, so the immediate Run() could hit
the already-running guard in startRun(). Run() returns that error without
logging it, the adapter replaced it with a generic message, and the tunnel
stayed installed with no engine behind it. A 0.3.4 debug bundle shows 15
such failed restarts in 13 days; six ended in a device-wide blackhole until
the user toggled the VPN.

- NetBirdAdapter.stop runs the blocking client.stop() on a dedicated serial
  queue and invokes the completion only after it returns; Client.Stop()
  already waits for the run loop to exit. The 15 s fallback and the
  listener-driven notifyStopCompleted are gone, and consecutive stops queue
  up instead of firing each other's handler.
- Without a completion the call stays synchronous, so the tvOS provider is
  unchanged. waitForExit: false uses StopWithoutWait, which stopTunnel now
  passes because iOS gives it only a few seconds.
- The start catch path no longer calls stop(): Stop() cancels whichever run
  is registered, so a late failure of a superseded run could kill the
  healthy one. The real Go error is forwarded instead of a fixed string.
- startTunnel resets adapter.isRestarting alongside isRestartInProgress.

* Keep the tvOS provider off the blocking stop path

restartClient stopped the client synchronously on the path monitor's queue,
so path updates and their debounce could not be delivered for the duration
of the Go teardown. Use the completion-based stop, which waits for the run
loop to exit on the adapter's own queue, and start the client from there.

stopTunnel passes waitForExit: false, as on iOS: the system gives it only a
few seconds and it already delays its completion by two more.

The restart error is logged as public so the forwarded Go error text is
readable in Console.
2026-09-09 18:01:37 +02:00
..