TunnelsManager: Handle waiting on a stale tunnel
If we have a stale tunnel on which we don't get status updates we rely
on a timer to update the status (see commit 34a7e5b
). Previously, if
the user tries to activate another tunnel, that resulted in both tunnels
waiting indefinitely. This commit fixes that.
This commit is contained in:
parent
efc593f7da
commit
fe69fe57e4
|
@ -24,6 +24,7 @@ class TunnelsManager {
|
|||
weak var tunnelsListDelegate: TunnelsManagerListDelegate?
|
||||
weak var activationDelegate: TunnelsManagerActivationDelegate?
|
||||
private var statusObservationToken: AnyObject?
|
||||
private var waiteeObservationToken: AnyObject?
|
||||
|
||||
init(tunnelProviders: [NETunnelProviderManager]) {
|
||||
tunnels = tunnelProviders.map { TunnelContainer(tunnel: $0) }.sorted { $0.name < $1.name }
|
||||
|
@ -205,6 +206,7 @@ class TunnelsManager {
|
|||
if let tunnelInOperation = tunnels.first(where: { $0.status != .inactive }) {
|
||||
wg_log(.info, message: "Tunnel '\(tunnel.name)' waiting for deactivation of '\(tunnelInOperation.name)'")
|
||||
tunnel.status = .waiting
|
||||
activateWaitingTunnelOnDeactivation(of: tunnelInOperation)
|
||||
if tunnelInOperation.status != .deactivating {
|
||||
startDeactivation(of: tunnelInOperation)
|
||||
}
|
||||
|
@ -232,6 +234,18 @@ class TunnelsManager {
|
|||
tunnels.forEach { $0.refreshStatus() }
|
||||
}
|
||||
|
||||
private func activateWaitingTunnelOnDeactivation(of tunnel: TunnelContainer) {
|
||||
waiteeObservationToken = tunnel.observe(\.status) { [weak self] tunnel, _ in
|
||||
guard let self = self else { return }
|
||||
if tunnel.status == .inactive {
|
||||
if let waitingTunnel = self.tunnels.first(where: { $0.status == .waiting }) {
|
||||
waitingTunnel.startActivation(activationDelegate: self.activationDelegate)
|
||||
}
|
||||
self.waiteeObservationToken = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func startObservingTunnelStatuses() {
|
||||
guard statusObservationToken == nil else { return }
|
||||
|
||||
|
@ -268,13 +282,6 @@ class TunnelsManager {
|
|||
}
|
||||
|
||||
tunnel.refreshStatus()
|
||||
|
||||
// In case some other tunnel is waiting for this tunnel to get deactivated
|
||||
if session.status == .disconnected || session.status == .invalid {
|
||||
if let waitingTunnel = self.tunnels.first(where: { $0.status == .waiting }) {
|
||||
waitingTunnel.startActivation(activationDelegate: self.activationDelegate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -308,18 +315,21 @@ class TunnelContainer: NSObject {
|
|||
var isAttemptingActivation = false {
|
||||
didSet {
|
||||
if isAttemptingActivation {
|
||||
self.activationTimer?.invalidate()
|
||||
let activationTimer = Timer(timeInterval: 5 /* seconds */, repeats: true) { [weak self] _ in
|
||||
guard let self = self else { return }
|
||||
self.refreshStatus()
|
||||
if self.status == .inactive || self.status == .active {
|
||||
self.isAttemptingActivation = false // This also invalidates the timer
|
||||
wg_log(.debug, message: "Status update notification timeout for tunnel '\(self.name)'. Tunnel status is now '\(self.tunnelProvider.connection.status)'.")
|
||||
switch self.tunnelProvider.connection.status {
|
||||
case .connected, .disconnected, .invalid:
|
||||
self.activationTimer?.invalidate()
|
||||
self.activationTimer = nil
|
||||
default:
|
||||
break
|
||||
}
|
||||
self.refreshStatus()
|
||||
}
|
||||
self.activationTimer = activationTimer
|
||||
RunLoop.main.add(activationTimer, forMode: .default)
|
||||
} else {
|
||||
activationTimer?.invalidate()
|
||||
activationTimer = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue