From 43ebbc31dfdc97adb284b8bcebcc3ae6689d053b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tor=20Arne=20Vestb=C3=B8?= Date: Tue, 9 Jun 2026 16:10:11 +0200 Subject: [PATCH] Report errors thrown inside tart run's fire-and-forget tasks We were discarding any error thrown inside these unstructured tasks, which silently hid failures to run the control socket or to start and stop the VM, and which the compiler now warns about. Wrap them in an ErrorReportingTask, which spawns the task and reports any thrown error to stderr, rather than repeating a do/catch at every call site. An unstructured task spawned from a synchronous context (a signal handler or SwiftUI action) has no parent to propagate the error to, so reporting it is the best we can do. --- Sources/tart/Commands/Run.swift | 10 +++++----- Sources/tart/Utils.swift | 18 ++++++++++++++++++ 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/Sources/tart/Commands/Run.swift b/Sources/tart/Commands/Run.swift index 2e89590..70d5515 100644 --- a/Sources/tart/Commands/Run.swift +++ b/Sources/tart/Commands/Run.swift @@ -514,7 +514,7 @@ struct Run: AsyncParsableCommand { } if #available(macOS 14, *) { - Task { + ErrorReportingTask("Failed to run control socket") { try await ControlSocket(vmDir.controlSocketURL).run() } } @@ -586,7 +586,7 @@ struct Run: AsyncParsableCommand { signal(SIGUSR2, SIG_IGN) let sigusr2Src = DispatchSource.makeSignalSource(signal: SIGUSR2) sigusr2Src.setEventHandler { - Task { + ErrorReportingTask("Failed to request guest OS to stop") { print("Requesting guest OS to stop...") try vm!.virtualMachine.requestStop() } @@ -798,13 +798,13 @@ struct MainApp: App { CommandGroup(replacing: .appInfo) { AboutTart(config: vm!.config) } CommandMenu("Control") { Button("Start") { - Task { try await vm!.virtualMachine.start() } + ErrorReportingTask("Failed to start VM") { try await vm!.virtualMachine.start() } } Button("Stop") { - Task { try await vm!.virtualMachine.stop() } + ErrorReportingTask("Failed to stop VM") { try await vm!.virtualMachine.stop() } } Button("Request Stop") { - Task { try vm!.virtualMachine.requestStop() } + ErrorReportingTask("Failed to request VM stop") { try vm!.virtualMachine.requestStop() } } if #available(macOS 14, *) { if (MainApp.suspendable) { diff --git a/Sources/tart/Utils.swift b/Sources/tart/Utils.swift index ddb54bb..1f2dc70 100644 --- a/Sources/tart/Utils.swift +++ b/Sources/tart/Utils.swift @@ -1,5 +1,23 @@ import Foundation +// A fire-and-forget task that reports any thrown error to stderr. An unstructured +// Task spawned from a synchronous context (a signal handler, a SwiftUI action) has +// no parent to propagate its error to, so we report it here instead of dropping it. +struct ErrorReportingTask { + let task: Task + + @discardableResult + init(_ context: String, operation: @escaping @Sendable () async throws -> Void) { + task = Task { + do { + try await operation() + } catch { + fputs("\(context): \(error)\n", stderr) + } + } + } +} + extension Collection { subscript (safe index: Index) -> Element? { indices.contains(index) ? self[index] : nil