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.

Signed-off-by: Roopesh Chander <roop@roopc.net>
This commit is contained in:
Roopesh Chander 2018-12-18 19:45:00 +05:30
parent fab7af6f38
commit 4e516d6769
1 changed files with 23 additions and 13 deletions

View File

@ -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
}
}
}