From 4e516d6769bd8152e9a846969a17b2a2c7b845f0 Mon Sep 17 00:00:00 2001 From: Roopesh Chander Date: Tue, 18 Dec 2018 19:45:00 +0530 Subject: 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 --- WireGuard/WireGuard/Tunnel/TunnelsManager.swift | 36 ++++++++++++++++--------- 1 file changed, 23 insertions(+), 13 deletions(-) (limited to 'WireGuard/WireGuard') diff --git a/WireGuard/WireGuard/Tunnel/TunnelsManager.swift b/WireGuard/WireGuard/Tunnel/TunnelsManager.swift index 10df606..26e134f 100644 --- a/WireGuard/WireGuard/Tunnel/TunnelsManager.swift +++ b/WireGuard/WireGuard/Tunnel/TunnelsManager.swift @@ -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 } } } -- cgit v1.2.3-59-g8ed1b