Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Conversation

@cbracken
Copy link
Member

@cbracken cbracken commented Dec 10, 2024

FlutterEngine at the _shell unique_ptr ivar it owns have different lifetimes. _shell is initialised transitively from runWithEntrypoint, and reset in [FlutterEngine destroyContext], which is called transitively from [FlutterviewController dealloc] via [FlutterEngine notifyViewControllerDeallocated].

As such, all uses of _shell should be checked either via an assertion, in cases we know the shell should be non-null, or via a runtime null check in cases where it's expected that it may be null.

Specifically, this guards against a crash that can occur if we get a CoreAnimation transaction commit callback for an inflight frame just as we're shutting down the app (or removing the FlutterView in an add-to-app scenario).

Example stack trace:

0  Flutter                        0x11b28 -[FlutterEngine platformView] + 53 (weak_ptr.h:53)
1  Flutter                        0x11994 -[FlutterEngine updateViewportMetrics:] + 186 (ref_ptr.h:186)
2  Flutter                        0x1f854 -[FlutterViewController updateViewportMetricsIfNeeded] + 427 (vector:427)
3  Flutter                        0x1f9b8 -[FlutterViewController viewDidLayoutSubviews] + 1411 (FlutterViewController.mm:1411)
4  UIKitCore                      0x8c864 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2376
5  QuartzCore                     0x1fa0c CA::Layer::layout_if_needed(CA::Transaction*) + 516
6  QuartzCore                     0x1ae84c CA::Context::commit_transaction(CA::Transaction*, double, double*) + 516
7  QuartzCore                     0x2888 CA::Transaction::commit() + 648

Issue: flutter/flutter#98735
Issue: flutter/flutter#159639

Pre-launch Checklist

  • I read the Contributor Guide and followed the process outlined there for submitting PRs.
  • I read the Tree Hygiene wiki page, which explains my responsibilities.
  • I read and followed the Flutter Style Guide and the C++, Objective-C, Java style guides.
  • I listed at least one issue that this PR fixes in the description above.
  • I added new tests to check the change I am making or feature I am adding, or the PR is test-exempt. See testing the engine for instructions on writing and running engine tests.
  • I updated/added relevant documentation (doc comments with ///).
  • I signed the CLA.
  • All existing and new tests are passing.

If you need help, consider asking for advice on the #hackers-new channel on Discord.

`FlutterEngine` at the `_shell` unique_ptr ivar it owns have different
lifetimes. `_shell` is initialised transitively from
`runWithEntrypoint`, and reset in `[FlutterEngine destroyContext]`,
which is called transitively from `[FlutterviewController dealloc]` via
`[FlutterEngine notifyViewControllerDeallocated]`.

As such, all uses of `_shell` should be checked either via an assertion,
in cases we know the shell should be non-null, or via a runtime null
check in cases where it's expected that it may be null.

Specifically, this guards against a crash that can occur if we get a
CoreAnimation transaction commit callback for an inflight frame just as
we're shutting down the app (or removing the FlutterView in an
add-to-app scenario).

Example stack trace:
```
0  Flutter                        0x11b28 -[FlutterEngine platformView] + 53 (weak_ptr.h:53)
1  Flutter                        0x11994 -[FlutterEngine updateViewportMetrics:] + 186 (ref_ptr.h:186)
2  Flutter                        0x1f854 -[FlutterViewController updateViewportMetricsIfNeeded] + 427 (vector:427)
3  Flutter                        0x1f9b8 -[FlutterViewController viewDidLayoutSubviews] + 1411 (FlutterViewController.mm:1411)
4  UIKitCore                      0x8c864 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2376
5  QuartzCore                     0x1fa0c CA::Layer::layout_if_needed(CA::Transaction*) + 516
6  QuartzCore                     0x1ae84c CA::Context::commit_transaction(CA::Transaction*, double, double*) + 516
7  QuartzCore                     0x2888 CA::Transaction::commit() + 648
```

Issue: flutter/flutter#159639
Copy link
Contributor

@hellohuanlin hellohuanlin left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 questions ask to learn

- (flutter::PlatformViewIOS*)iosPlatformView {
FML_DCHECK(_shell);
if (!_shell) {
return nullptr;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

curious why we use WeakPtr in previous line, but raw pointer here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and RefPtr below? (Should they be WeakPtr as well?)

Copy link
Member Author

@cbracken cbracken Dec 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use cases:

  • WeakPtr: A raw pointer wrapper that, in unopt builds, asserts that all dereferences occur on the thread on which it was allocated. We use this to ensure that all derefs of shell occur on the platform thread. Does not do refcounting. We use this where we want to enforce threading invariants but object ownership/lifetime is managed by someone else.
  • RefPtr: A refcounted pointer. We use this in the engine where an object ownership is shared between a pool of owners. You could think of this as somewhat similar to std::shared_ptr. There's other utility code in ref_counted.h built around this that supports threading invariants.
  • raw C/C++ pointers: In cases where we want a non-owning pointer to an object whose lifetime is managed by someone else and where we don't care about threading invariants, we often use raw C/C++ pointers.

When I wrote this patch, I looked and these getters are only really used by FlutterEngine itself, FlutterViewController, and tests. There's no reason we couldn't be consistent and return at raw pointer instead of the WeakPtr in the platformView getter, but that's orthogonal to this fix.

My thinking is that rather than do that though, I'd rather send a followup that eliminates these getters altogether and provides better API instead. For example, engine.iosPlatformView is used by accessibility code in FlutterViewController; the relevant non-view-specific code should simply be refactored into FlutterEngine instead.

@cbracken cbracken added the autosubmit Merge PR when tree becomes green via auto submit App label Dec 10, 2024
@auto-submit auto-submit bot merged commit 23fc43d into flutter:main Dec 10, 2024
30 checks passed
@cbracken cbracken deleted the null-check-shell branch December 10, 2024 19:07
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request Dec 10, 2024
github-merge-queue bot pushed a commit to flutter/flutter that referenced this pull request Dec 10, 2024
…160044)

flutter/engine@58e5f9b...23fc43d

2024-12-10 chris@bracken.jp iOS: Add null checks on shell dereference
(flutter/engine#57099)
2024-12-10 Breakthrough@users.noreply.github.com [engine] Migrate
fuchsia.io Open functions to Open3 (flutter/engine#56818)

If this roll has caused a breakage, revert this CL and stop the roller
using the controls here:
https://autoroll.skia.org/r/flutter-engine-flutter-autoroll
Please CC jonahwilliams@google.com,zra@google.com on the revert to
ensure that a human
is aware of the problem.

To file a bug in Flutter:
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
auto-submit bot pushed a commit that referenced this pull request Dec 12, 2024
Eliminates some cases where `FlutterViewController` was relying on `FlutterEngine` internals:
* `[FlutterEngine shell]`
* `[FlutterEngine platformView]`
* `[FlutterEngine iosPlatformView]`

Instead, `FlutterEngine` now exposes:
* `installFirstFrameCallback:`
* `enableSemantics:withFlags:`
* `notifyViewCreated`
* `notifyViewDestroyed`
* `waitForFirstFrameSync:callback:`

Also fixes a couple cases where we were relying on transitive header includes:
* `FlutterAppController` relied on `FlutterViewController_Internal.h` for `sendDeepLinkToFramework:completionHandler:`

This is a refactoring followup to #57099 that introduces no semantic changes.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
nick9822 pushed a commit to nick9822/flutter that referenced this pull request Dec 18, 2024
`FlutterEngine` at the `_shell` unique_ptr ivar it owns have different lifetimes. `_shell` is initialised transitively from `runWithEntrypoint`, and reset in `[FlutterEngine destroyContext]`, which is called transitively from `[FlutterviewController dealloc]` via `[FlutterEngine notifyViewControllerDeallocated]`.

As such, all uses of `_shell` should be checked either via an assertion, in cases we know the shell should be non-null, or via a runtime null check in cases where it's expected that it may be null.

Specifically, this guards against a crash that can occur if we get a CoreAnimation transaction commit callback for an inflight frame just as we're shutting down the app (or removing the FlutterView in an add-to-app scenario).

Example stack trace:
```
0  Flutter                        0x11b28 -[FlutterEngine platformView] + 53 (weak_ptr.h:53)
1  Flutter                        0x11994 -[FlutterEngine updateViewportMetrics:] + 186 (ref_ptr.h:186)
2  Flutter                        0x1f854 -[FlutterViewController updateViewportMetricsIfNeeded] + 427 (vector:427)
3  Flutter                        0x1f9b8 -[FlutterViewController viewDidLayoutSubviews] + 1411 (FlutterViewController.mm:1411)
4  UIKitCore                      0x8c864 -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 2376
5  QuartzCore                     0x1fa0c CA::Layer::layout_if_needed(CA::Transaction*) + 516
6  QuartzCore                     0x1ae84c CA::Context::commit_transaction(CA::Transaction*, double, double*) + 516
7  QuartzCore                     0x2888 CA::Transaction::commit() + 648
```

Issue: flutter#98735
Issue: flutter#159639

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
nick9822 pushed a commit to nick9822/flutter that referenced this pull request Dec 18, 2024
Eliminates some cases where `FlutterViewController` was relying on `FlutterEngine` internals:
* `[FlutterEngine shell]`
* `[FlutterEngine platformView]`
* `[FlutterEngine iosPlatformView]`

Instead, `FlutterEngine` now exposes:
* `installFirstFrameCallback:`
* `enableSemantics:withFlags:`
* `notifyViewCreated`
* `notifyViewDestroyed`
* `waitForFirstFrameSync:callback:`

Also fixes a couple cases where we were relying on transitive header includes:
* `FlutterAppController` relied on `FlutterViewController_Internal.h` for `sendDeepLinkToFramework:completionHandler:`

This is a refactoring followup to flutter/engine#57099 that introduces no semantic changes.

[C++, Objective-C, Java style guides]: https://github.com/flutter/engine/blob/main/CONTRIBUTING.md#style
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

autosubmit Merge PR when tree becomes green via auto submit App platform-ios

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants