From dfbd3d06764340c2d739dabc6ab782e29a8f0791 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Fri, 17 Jan 2025 15:54:46 +1100 Subject: [PATCH 1/2] chore: gracefully shutdown app --- .../Coder Desktop/Coder_DesktopApp.swift | 8 ++++ .../Preview Content/PreviewVPN.swift | 45 +++++++++++++------ Coder Desktop/Coder Desktop/VPNService.swift | 38 ++++++++++++---- .../Coder Desktop/Views/VPNMenu.swift | 5 +-- 4 files changed, 70 insertions(+), 26 deletions(-) diff --git a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift index 408722b..4b06aab 100644 --- a/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift +++ b/Coder Desktop/Coder Desktop/Coder_DesktopApp.swift @@ -37,6 +37,14 @@ class AppDelegate: NSObject, NSApplicationDelegate { } } + func applicationShouldTerminate(_: NSApplication) -> NSApplication.TerminateReply { + Task { + await vpn.stop() + NSApp.reply(toApplicationShouldTerminate: true) + } + return .terminateLater + } + func applicationShouldTerminateAfterLastWindowClosed(_: NSApplication) -> Bool { false } diff --git a/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift b/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift index 04f37c0..ed9b73e 100644 --- a/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift +++ b/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift @@ -24,27 +24,46 @@ final class PreviewVPN: Coder_Desktop.VPNService { self.shouldFail = shouldFail } + var startTask: Task? func start() async { - state = .connecting - do { - try await Task.sleep(for: .seconds(10)) - } catch { - state = .failed(.longTestError) + if await startTask?.value != nil { return } - state = shouldFail ? .failed(.longTestError) : .connected + + startTask = Task { + state = .connecting + do { + try await Task.sleep(for: .seconds(5)) + } catch { + state = .failed(.longTestError) + return + } + state = shouldFail ? .failed(.longTestError) : .connected + } + defer { startTask = nil } + await startTask?.value } + var stopTask: Task? func stop() async { - guard state == .connected else { return } - state = .disconnecting - do { - try await Task.sleep(for: .seconds(10)) - } catch { - state = .failed(.longTestError) + await startTask?.value + guard state == .connected else { return} + if await stopTask?.value != nil { return } - state = .disabled + + stopTask = Task { + state = .disconnecting + do { + try await Task.sleep(for: .seconds(5)) + } catch { + state = .failed(.longTestError) + return + } + state = .disabled + } + defer { stopTask = nil } + await stopTask?.value } func configureTunnelProviderProtocol(proto _: NETunnelProviderProtocol?) { diff --git a/Coder Desktop/Coder Desktop/VPNService.swift b/Coder Desktop/Coder Desktop/VPNService.swift index cb5cf55..0ec7618 100644 --- a/Coder Desktop/Coder Desktop/VPNService.swift +++ b/Coder Desktop/Coder Desktop/VPNService.swift @@ -63,20 +63,40 @@ final class CoderVPNService: NSObject, VPNService { installSystemExtension() } + var startTask: Task? func start() async { - tunnelState = .connecting - await enableNetworkExtension() + if await startTask?.value != nil { + return + } + startTask = Task { + tunnelState = .connecting + await enableNetworkExtension() - // TODO: enable communication with the NetworkExtension to track state and agents. For - // now, just pretend it worked... - tunnelState = .connected + // TODO: enable communication with the NetworkExtension to track state and agents. For + // now, just pretend it worked... + tunnelState = .connected + } + defer { startTask = nil } + await startTask?.value } + var stopTask: Task? func stop() async { - tunnelState = .disconnecting - await disableNetworkExtension() - // TODO: determine when the NetworkExtension is completely disconnected - tunnelState = .disabled + // Wait for a start operation to finish first + await startTask?.value + guard state == .connected else { return } + if await stopTask?.value != nil { + return + } + stopTask = Task { + tunnelState = .disconnecting + await disableNetworkExtension() + + // TODO: determine when the NetworkExtension is completely disconnected + tunnelState = .disabled + } + defer { stopTask = nil } + await stopTask?.value } func configureTunnelProviderProtocol(proto: NETunnelProviderProtocol?) { diff --git a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift index e3a6459..8788936 100644 --- a/Coder Desktop/Coder Desktop/Views/VPNMenu.swift +++ b/Coder Desktop/Coder Desktop/Views/VPNMenu.swift @@ -59,10 +59,7 @@ struct VPNMenu: View { }.buttonStyle(.plain) TrayDivider() Button { - Task { - await vpn.stop() - NSApp.terminate(nil) - } + NSApp.terminate(nil) } label: { ButtonRowView { Text("Quit") From 72265a3466c3ceb9b356a50cc20444d06efb1098 Mon Sep 17 00:00:00 2001 From: Ethan Dickson Date: Fri, 17 Jan 2025 15:55:43 +1100 Subject: [PATCH 2/2] fmt --- Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift b/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift index ed9b73e..91900b8 100644 --- a/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift +++ b/Coder Desktop/Coder Desktop/Preview Content/PreviewVPN.swift @@ -47,7 +47,7 @@ final class PreviewVPN: Coder_Desktop.VPNService { var stopTask: Task? func stop() async { await startTask?.value - guard state == .connected else { return} + guard state == .connected else { return } if await stopTask?.value != nil { return }