From dce7ffe4f09ada9a1cd982376cb36e8dabf44f62 Mon Sep 17 00:00:00 2001 From: Thomas Eizinger Date: Tue, 10 Jun 2025 09:09:16 +0200 Subject: [PATCH] fix(apple): drop `Session` in a new task (#9478) Until we implement #3959 for the Apple client, we need to be careful around how we de-initialise the Rust session. Callback-based designs are difficult to get right across boundaries because they enable re-entrances which then lead to runtime errors. Specifically, freeing the session needs to cleanup the tokio runtime but that is impossible if the callback is still executed from that exact runtime. To workaround this, we need to free the session pointer from a new task. Moving to #3959 will solve this in a much more intuitive way because we can ditch the callbacks and instead move to a stream of events that the host app can consume. --------- Signed-off-by: Thomas Eizinger --- swift/apple/FirezoneNetworkExtension/Adapter.swift | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/swift/apple/FirezoneNetworkExtension/Adapter.swift b/swift/apple/FirezoneNetworkExtension/Adapter.swift index 45cf357ee..cc905f394 100644 --- a/swift/apple/FirezoneNetworkExtension/Adapter.swift +++ b/swift/apple/FirezoneNetworkExtension/Adapter.swift @@ -416,15 +416,17 @@ extension Adapter: CallbackHandlerDelegate { } func onDisconnect(error: DisconnectError) { - // Immediately invalidate our session pointer to prevent workQueue items from trying to use it. - // Assigning to `nil` will invoke `Drop` on the Rust side. - session = nil - // Since connlib has already shutdown by this point, we queue this callback // to ensure that we can clean up even if connlib exits before we are done. workQueue.async { [weak self] in guard let self = self else { return } + // Immediately invalidate our session pointer to prevent workQueue items from trying to use it. + // Assigning to `nil` will invoke `Drop` on the Rust side. + // This must happen asynchronously and not as part of the callback to allow Rust to break + // cyclic dependencies between the runtime and the task that is executing the callback. + self.session = nil + // If auth expired/is invalid, delete stored token and save the reason why so the GUI can act upon it. if error.isAuthenticationError() { do {