Repository navigation
iOS: Reject merged-platform-ui-thread=mergeAfterLaunch - #190051
Merged
Merged
Conversation
iOS runs with the platform and UI threads merged from launch and only implements fully-merged (`kEnabled`) threading. The `mergeAfterLaunch` approach starts the engine on a separate UI thread and merges it onto the platform thread once the root isolate is running. The `mergeAfterLaunch` merge/unmerge code lives entirely in `shell/common`. The iOS embedder has never implemented it, and no iOS code has ever referenced it. On iOS, the flag was never wired up, and it isn't reachable through any supported configuration. The `FLTEnableMergedPlatformUIThread` Info.plist key only ever selects `kEnabled` or `kDisabled`. The only way to reach it is the raw command-line flag, which nothing rejects today, so passing `--merged-platform-ui-thread=mergeAfterLaunch` slips through and produces a broken engine since: * `Shell::Spawn` refuses `kMergeAfterLaunch` and returns null, so `FlutterEngineGroup` fails to spawn secondary engines outright. * The CADisplayLink-backed vsync waiter registers on the run loop of whichever thread services the UI task runner when it's constructed. Under the merged model iOS actually ships (`kEnabled`) that's the platform thread, which is correct. Under `mergeAfterLaunch` it would be the temporary UI thread that the model abandons after launch, and which won't work. This has been unsupported since `mergeAfterLaunch` was introduced. Since every iOS `Settings` setting passes through `FlutterDartProject`, we can fail startup there with an `FML_CHECK` if this configuration is set. Normally this would live in `shell/common/switches.cc` but Android also passes `require_merged_platform_ui_thread` to `SettingsFromCommandLine` and still supports `mergeAfterLaunch` as a launch-latency optimization used by internal customers. Also added a test that hints that `mergeAfterLaunch` is an intentional, supported configuration so that people like me don't try to lock it down again. See: flutter#189893 This is part of cleanup work intended to simplify the iOS embedder prior to an eventual migration to the embedder API. Issue: flutter#112232
Contributor
There was a problem hiding this comment.
Code Review
This pull request adds a unit test to verify that the mergeAfterLaunch option for the merged-platform-ui-thread switch is correctly parsed. Additionally, it introduces an assertion in the iOS platform code to explicitly disallow this option on iOS, causing a crash if it is specified. There are no review comments, and I have no feedback to provide.
jason-simmons
approved these changes
Jul 27, 2026
auto-submit Bot
pushed a commit
to flutter/packages
that referenced
this pull request
Jul 28, 2026
flutter/flutter@9988960...0f02463 2026-07-28 [email protected] Roll Fuchsia Test Scripts from E8hJ1AfK8CtGtaES0... to 1frGe_KltAJKkeyPg... (flutter/flutter#190134) 2026-07-28 [email protected] iOS: Reject merged-platform-ui-thread=mergeAfterLaunch (flutter/flutter#190051) 2026-07-28 [email protected] iOS: Migrate TaskRunner tests to Swift Testing (flutter/flutter#190055) 2026-07-28 [email protected] Run Mac golden tests on ARM bots (flutter/flutter#189465) 2026-07-28 [email protected] iOS,macOS: Rename Swift test files to end in Tests.swift (flutter/flutter#190063) 2026-07-28 [email protected] Fix hcpp cliprect being behind by 1 frame when scrolling (flutter/flutter#189946) 2026-07-28 [email protected] Roll Fuchsia Linux SDK from vpboK5fPPIoFteqRq... to OZkZC_2CZ_G5rbMIS... (flutter/flutter#190115) 2026-07-27 [email protected] Add Ishaq Hassan to AUTHORS (flutter/flutter#190064) 2026-07-27 [email protected] [wimp] fixes ubo padding size issue (flutter/flutter#189958) 2026-07-27 [email protected] Roll pub packages (flutter/flutter#189872) 2026-07-27 [email protected] Move tool host_cross_arch tests into different shards (flutter/flutter#189470) 2026-07-27 49699333+dependabot[bot]@users.noreply.github.com Bump actions/labeler from 6.2.0 to 7.0.0 in the all-github-actions group (flutter/flutter#190099) 2026-07-27 [email protected] [ios]do not nuke user input path when running uiscene integration test (flutter/flutter#186436) 2026-07-27 [email protected] ci: verify_binaries_pre_codesigned part 2 (flutter/flutter#190078) 2026-07-27 [email protected] Roll Abseil to ff6e8ce3e932 (flutter/flutter#189998) 2026-07-27 [email protected] Android_hardware_smoke_test: clean up golden copy in CI (flutter/flutter#189948) If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-packages Please CC [email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Packages: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://issues.skia.org/issues/new?component=1389291&template=1850622 Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
cbracken
added a commit
to cbracken/flutter
that referenced
this pull request
Aug 2, 2026
-waitForFirstFrameSync: is unconditionally a no-op on iOS, and that is enforced by the tree rather than by convention. The only constructible threading configuration merges the platform and UI task runners onto the platform thread (`FlutterDartProject.mm` passes `require_merged_platform_ui_thread` to `SettingsFromCommandLine`, and flutter#190051 rejects `mergeAfterLaunch` as well), so the thread calling this method at first layout is the thread responsible for producing the frame, and `Shell::WaitForFirstFrame` always takes its `kFailedPrecondition` early return without waiting. Removes the method and its only call site in FlutterViewController's first layout; the surface creation that shared the call site is unchanged. This was the main-thread wait introduced in flutter-team-archive/engine#9506 to avoid presenting a black first frame when transitioning into a FlutterViewController with a prewarmed engine (flutter#32937). It has been inert since the platform and UI threads merged: a thread cannot block waiting for a frame it must itself produce. If the black flash proves reproducible under the merged configuration it will need a mechanism other than blocking, e.g. deferring presentation until a frame is available. Verified on a physical device (release mode) that repeatedly presenting a FlutterViewController on a prewarmed engine shows no flash of black with the wait removed. No test changes: the wait and its call site had no coverage, and the removed behaviour is unreachable in any constructible iOS configuration.
10 tasks done
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.
iOS runs with the platform and UI threads merged from launch and only implements fully-merged (
kEnabled) threading. ThemergeAfterLaunchapproach starts the engine on a separate UI thread and merges it onto the platform thread once the root isolate is running.The
mergeAfterLaunchmerge/unmerge code lives entirely inshell/common. The iOS embedder has never implemented it, and no iOS code has ever referenced it. On iOS, the flag was never wired up, and it isn't reachable through any supported configuration. TheFLTEnableMergedPlatformUIThreadInfo.plist key only ever selectskEnabledorkDisabled.The only way to reach it is the raw command-line flag, which nothing rejects today, so passing
--merged-platform-ui-thread=mergeAfterLaunchslips through and produces a broken engine since:Shell::SpawnrefuseskMergeAfterLaunchand returns null, soFlutterEngineGroupfails to spawn secondary engines outright.kEnabled) that's the platform thread, which is correct. UndermergeAfterLaunchit would be the temporary UI thread that the model abandons after launch, and which won't work.This has been unsupported since
mergeAfterLaunchwas introduced.Since every iOS
Settingssetting passes throughFlutterDartProject, we can fail startup there with anFML_CHECKif this configuration is set. Normally this would live inshell/common/switches.ccbut Android also passesrequire_merged_platform_ui_threadtoSettingsFromCommandLineand still supportsmergeAfterLaunchas a launch-latency optimization used by internal customers.Also added a test that hints that
mergeAfterLaunchis an intentional, supported configuration so that people like me don't try to lock it down again. See: #189893This is part of cleanup work intended to simplify the iOS embedder prior to an eventual migration to the embedder API.
Issue: #112232
Pre-launch Checklist
///).If you need help, consider asking for advice on the #hackers-new channel on Discord.
If this change needs to override an active code freeze, provide a comment explaining why. The code freeze workflow can be overridden by code reviewers. See pinned issues for any active code freezes with guidance.
Note: The Flutter team is currently trialing the use of Gemini Code Assist for GitHub. Comments from the
gemini-code-assistbot should not be taken as authoritative feedback from the Flutter team. If you find its comments useful you can update your code accordingly, but if you are unsure or disagree with the feedback, please feel free to wait for a Flutter team member's review for guidance on which automated comments should be addressed.