fix(connectivity_plus): guard eventSink after engine teardown (iOS)#3797
Open
friebetill wants to merge 1 commit intofluttercommunity:mainfrom
Open
fix(connectivity_plus): guard eventSink after engine teardown (iOS)#3797friebetill wants to merge 1 commit intofluttercommunity:mainfrom
friebetill wants to merge 1 commit intofluttercommunity:mainfrom
Conversation
…n on iOS NWPathMonitor can fire a connectivity update while the app is in the background, after the FlutterEngine's shell has been torn down. The DispatchQueue.main.async block then invokes the stale FlutterEventSink, which calls -[FlutterEngine sendOnChannel:] and hits an NSAssertion at FlutterEngine.mm:1315, aborting the process with SIGABRT. Guard the main-queue emission with [weak self], a local eventSink copy, and a UIApplication.applicationState != .background check so that pending updates are dropped instead of propagated into a dead engine. The next onListen / check call after foregrounding will resync state.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
On iOS,
connectivity_pluscan crash the host app withSIGABRTwhenNWPathMonitorfires a connectivity update while the app is in the background and theFlutterEngine's shell has already been torn down. The pendingDispatchQueue.main.asyncblock still invokes the staleFlutterEventSink, which trips anNSAssertin-[FlutterEngine sendOnChannel:message:binaryReply:]and aborts the process.This patch guards the main-queue emission with a weak
self, a localeventSinkcopy, and anUIApplication.applicationState != .backgroundcheck, so pending updates are dropped instead of being forwarded into a dead engine.Observed in production (Sentry) on iOS 18.7.7 across three separate crashes in one week, all with the same stack and
Role: Non UIin the crash report.Root cause
ConnectivityPlusPlugin.connectivityUpdateHandlerhops to the main queue before emitting the path update:When the app is backgrounded, the Flutter engine's
_shellmay already have been destroyed by the time the block runs.self.eventSinkis still non-nil (the engine hasn't finished detaching), so the call reaches-[FlutterEngine sendOnChannel:…], which asserts atFlutterEngine.mm:1315and aborts.detachFromEnginenil-ingeventSinkis not a reliable fence here: it is not guaranteed to run before the pending main-queue block executes, and there is a race window between engine shutdown and the detach callback.Fix
selfweakly in theDispatchQueue.main.asyncblock, so the plugin can be released cleanly during teardown.eventSinkinto a local constant inside the block and bail out if it isnil.UIApplication.shared.applicationState == .background. The engine's shell can be torn down while backgrounded, and forwarding events there is unsafe. The nextonListen/checkcall after foregrounding will resync state, so no information is lost.Only the iOS plugin source is touched. The macOS plugin lives in a separate file and is unaffected.
Stack trace
Related issues / PRs
crash on iOS: Sending a message before the FlutterEngine has been run.(closed, originally reported fixed in 2.3.5). This PR addresses what looks like a regression of the same signature.fix(connectivity_plus): Use serial queue for NWPathMonitor to prevent race condition crash(merged). That change movedNWPathMonitorto a private serial queue and resolved an adjacentswift_deallocPartialClassInstancecrash, but it did not guard theeventSinkemission against a torn-down engine, which is the case this PR handles.Testing
No automated regression test. The crash is a race during engine teardown and I don't have a reliable way to reproduce it in the plugin's unit tests.
Manually validated:
Connectivity().onConnectivityChangedon an iOS device.Before the fix: reproducible
SIGABRTwith the stack above within a few toggles. After the fix: no crash; on foreground, the subscription delivers the current connectivity state via the existingcheckConnectivity/onListenflow.