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

Conversation

@dkwingsmt
Copy link
Contributor

@dkwingsmt dkwingsmt commented May 10, 2023

This PR adapts FlutterThreadSynchronizer to multi-view.

Problem

The FlutterThreadSynchronizer is a macOS engine class to ensure that, if the current window is resized, nothing will not be presented until the layer tree is drawn with the up-to-date size.

This class is not compatible with multiview. A simple way to realize it is: while the class is owned by FlutterView, it blocks the global platform thread and rasterizer thread - there is got to be some problems. Indeed, when I was testing with multiple windows (#40399), the app freezes as soon as I resize a window.

The problem is because Flutter only have one rasterizer thread. When I'm resizing window A, A's synchronizer detects that the size mismatches, so it blocks the rasterizer thread to wait for an updated content with the updated size. However, window B is to be rendered next, and B's size matches and will try to rasterize, and will be blocked due to the blocked rasterizer thread, window A's updated content will never arrive, causing a deadlock.

Changes

This PR removes the single-view assumption of FlutterThreadSynchronizer by making it created by FlutterEngine and shared by all FlutterViews, and that it prevents rasterization for all views if any view has a mismatched size.

The synchronizer is assigned to the view controller in the attachToEngine:withId method (now renamed to setUpWithEngine:viewId:threadSynchronizer:.

This PR also adds unit tests for FlutterThreadSynchronizer, which didn't have any unit tests at all.

  • To achieve this, the beginResizeForView: method no longer checks whether is called on the main thread, but whether it's called on the main queue. These are equivalent for the real main queue, since the documentation promises that the main queue always executes on the main thread. However, this change allows substituting the queue with a custom one for unit tests.

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 Hixie said 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.

@dkwingsmt dkwingsmt requested review from knopp and removed request for knopp May 10, 2023 22:19
@dkwingsmt
Copy link
Contributor Author

Actually let me investigate if I can only halt one view's rendering instead of all views'.

@knopp
Copy link
Member

knopp commented May 11, 2023

I think sharing the thread synchronizer is the way to go, but it should probably be aware of multiview.

So the API should look maybe something like:

@interface FlutterThreadSynchronizer : NSObject

- (void)view:(ViewId)viewId beginResize:(CGSize)size notify:(nonnull dispatch_block_t)notify;
- (void)view:(ViewId)viewId performCommit:(CGSize)size notify:(nonnull dispatch_block_t)notify;
- (void)viewShutDown:(ViewId)viewId

The waiting for content size would become a Map<ViewId, CGSize>.

beginResize would block until all pending frames for ViewId->Size are satisfied.
performCommit would update the size for given viewId.

The _shuttingDown however would probably also needed to be tracked per view.

@dkwingsmt
Copy link
Contributor Author

dkwingsmt commented May 11, 2023

@knopp Yeah, that's what I did in this PR. The method name is a little different, but I can switch to yours.

I don't think shutdown should be tracked per view, because [FlutterView shutdown] is only set when the engine is disposed. We can actually remove [FlutterView shutdown].

And after thinking for a while, I don't think it's possible to allow other views to rendering while one view is waiting for its correctly sized content. The rasterization for the view being resized has to fully complete before the next view can be resized, since after all the rasterization is not made async. However, this does not harm the experience at all, since the wait time should be minimal. All views continue their animation while the view is being resized.

The remaining question is, can we add a unit test? I think it'll need multi-threading (multiple task queues), but I'm not familiar with it. Can you give me some ideas?

}

- (void)deregisterView:(int64_t)viewId {
_contentSizes.erase(viewId);
Copy link
Member

Choose a reason for hiding this comment

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

Is this meant to be called from platform thread? If not then maybe it should take the lock and notify _condBlockResize if it is actively waiting for content.

Copy link
Contributor Author

@dkwingsmt dkwingsmt May 24, 2023

Choose a reason for hiding this comment

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

I've added assertion that this method is called from the platform thread.

@knopp
Copy link
Member

knopp commented May 12, 2023

My bad. The API seems fine, though I'm not sure what the cleanup process is.

As for the tests, there are still just two threads, right? (platform and raster). I'd just use a serial background dispatch queue for simulating raster thread (dispatch_queue_create with DISPATCH_QUEUE_SERIAL).

@chinmaygarde chinmaygarde requested a review from iskakaushik May 18, 2023 20:15
@dkwingsmt
Copy link
Contributor Author

@knopp I've updated this PR and it should be ready for review. Changes include:

  • Unit tests for the synchronizer, including its single-view capability and multiview capability.
  • It no longer checks whether beginResizeForView: is called on the main thread, but whether it's called on the main queue.
    • For the real main queue, these are equivalent, since the documentation said that the main queue always executes on the main thread. However, this change allows substituting the queue with a custom one for unit tests.
  • Documentation updates.
  • The thread synchronizer is now assigned to FlutterViewController via the setupWithEngine:viewId:threadSynchronizer method.

@dkwingsmt dkwingsmt requested a review from cbracken May 24, 2023 07:00
@dkwingsmt
Copy link
Contributor Author

dkwingsmt commented May 24, 2023

Also cc @cbracken might want to take a look as well.

cc @loic-sharma since Windows might suffer from similar single-view assumption on a thread synchronizer.

Copy link
Member

@knopp knopp left a comment

Choose a reason for hiding this comment

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

Some nits, otherwise LGTM!

@dkwingsmt dkwingsmt added the autosubmit Merge PR when tree becomes green via auto submit App label May 25, 2023
@auto-submit auto-submit bot removed the autosubmit Merge PR when tree becomes green via auto submit App label May 25, 2023
@auto-submit
Copy link
Contributor

auto-submit bot commented May 25, 2023

auto label is removed for flutter/engine, pr: 41915, due to - The status or check suite Linux Web Framework tests has failed. Please fix the issues identified (or deflake) before re-applying this label.

@dkwingsmt dkwingsmt added the autosubmit Merge PR when tree becomes green via auto submit App label May 25, 2023
@auto-submit auto-submit bot merged commit f4dc96a into flutter:main May 25, 2023
@dkwingsmt dkwingsmt deleted the multiview-thread-synchronizer branch May 25, 2023 22:51
engine-flutter-autoroll added a commit to engine-flutter-autoroll/flutter that referenced this pull request May 25, 2023
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-macos

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants