Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make Shell::Create not blocking platform thread by asynchronously setup shell subsystems #19641

Closed

Conversation

scutlight
Copy link
Member

Description

As the related issue refer, the application may be doing too much work on its main thread even in a simple hello_world demo.
That is because the creation of engine on the UI thread takes a noticeable time, and it is blocking the main thread in order to run Shell::Setup synchronously.
This PR breaks the main thread blocking, by introducing std::async to wait for the shell subsystems creation in a background thread. Shell::Setup will be executed when the subsystems are ready. Before shell is setup, calls to shell subsystem will be pushed into a pending tasks queue which would be run in sequence once the setup is done.
Code changes are mainly inside Shell::CreateShellOnPlatformThread and Shell::Setup.

Related Issues

flutter/flutter#40563 could be resolved by this PR.

Tests

I added the following tests:

Replace this with a list of the tests that you added as part of this PR. A
change in behaviour with no test covering it will likely get reverted
accidentally sooner or later. PRs must include tests for all
changed/updated/fixed behaviors. See testing the engine for instructions on
writing and running engine tests.

Checklist

Before you create this PR confirm that it meets all requirements listed below by checking the relevant checkboxes ([x]). This will ensure a smooth and quick review process.

  • I read the contributor guide and followed the process outlined there for submitting PRs.
  • I signed the CLA.
  • I read and followed the C++, Objective-C, Java style guides for the engine.
  • I read the tree hygiene wiki page, which explains my responsibilities.
  • I updated/added relevant documentation.
  • All existing and new tests are passing.
  • I am willing to follow-up on review comments in a timely manner.

Breaking Change

Did any tests fail when you ran them? Please read handling breaking changes.

@fluttergithubbot
Copy link
Contributor

It looks like this pull request may not have tests. Please make sure to add tests before merging. If you need an exemption to this rule, contact Hixie on the #hackers channel in Chat.

Reviewers: Read the Tree Hygiene page and make sure this patch meets those guidelines before LGTMing.

@auto-assign auto-assign bot requested a review from liyuqian July 10, 2020 08:58
@liyuqian liyuqian added the severe: performance Relates to speed or footprint issues. label Jul 13, 2020
Copy link
Contributor

@liyuqian liyuqian left a comment

Choose a reason for hiding this comment

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

This idea looks very interesting to me. From users' perspective, this PR is great as it brings async setup to them in a completely transparent way and there's no extra work for them to receive the benefit. On the other hand, I'm a little concerned by how many pieces of code like https://github.com/scutlight/engine/blob/b791479e926f75c883c0d93251fe780acf69e30f/shell/common/shell.cc#L660-L671 need to be injected not just now, but also for every new API added to Shell in the future.

@gaaclarke @chinmaygarde @jason-simmons : what's your thought on this? Shall we give Shell users some extra work if they want async setup so we can leave most Shell functions intact (#18047), or shall we give no extra work to Shell users but modify most Shell functions for async support (this PR)?

@@ -61,16 +61,18 @@ std::unique_ptr<Shell> Shell::CreateShellOnPlatformThread(
std::promise<fml::WeakPtr<SnapshotDelegate>> snapshot_delegate_promise;
Copy link
Contributor

Choose a reason for hiding this comment

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

If we're going to land this, Shell::CreateShellOnPlatformThread should be properly documented by stating that the Shell returned may not be ready yet due to asynchronous setup but all functions can be called as normal because we'll defer them in a queue until the shell is set up.

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure thing, only if we've reach agreement on this asynchronous setup idea. And there are many failed test cases to be updated due to asynchronous setup.

@liyuqian liyuqian added the perf: speed Performance issues related to (mostly rendering) speed label Jul 23, 2020
// creation and then setup the shell.
auto result = std::async(
std::launch::async,
[shell = shell.get(), platform_view = std::move(platform_view),
Copy link
Member

Choose a reason for hiding this comment

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

Sending a raw pointer shell to an async operation is not going to cut it. You'll need a shared_ptr.

Copy link
Member Author

Choose a reason for hiding this comment

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

I'm not quite sure whether I've caught you. What's the meaning of 'cut it'? Do you mean that shell might have been destructed while executing the async operation? In that case, shall I use shell = shell->weak_factory_.GetWeakPtr() instead?

Copy link
Member

Choose a reason for hiding this comment

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

Sorry, "not going to cut it" is an english idiom that means "isn't going to be adequate".

Yes, the shell could be deleted before this operation or during this operation. A weak pointer isn't going to be good enough. In the case they are happening at the same time you'd need to block the deleting thread until this has completed. The easiest solution is using a shared_ptr.

@@ -391,6 +391,8 @@ class Shell final : public PlatformView::Delegate,
>
service_protocol_handlers_;
bool is_setup_ = false;
std::future<bool> async_waiting_subsystems_;
std::queue<std::future<void>> pending_tasks_before_setup_;
Copy link
Member

Choose a reason for hiding this comment

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

You are editing and reading this queue from multiple threads, but it's not a threadsafe datastructure.

Copy link
Member Author

Choose a reason for hiding this comment

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

pending_tasks_before_setup_ is only access on platform thread, it does not need to be thread safe.

Copy link
Member

Choose a reason for hiding this comment

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

I read through the code and I think you are right. It would be nice to enforce this.

@gaaclarke
Copy link
Member

I think there is some promise in this approach since it is taking advantage of the async design of RunEngine. I'm not too happy with that design but this doesn't make things worse.

This particular implementation, as it is today, is perilous. I've added some comments on how to make it safe.

@scutlight scutlight requested a review from gaaclarke July 28, 2020 10:00
@scutlight
Copy link
Member Author

@liyuqian @gaaclarke : Thanks for your reviews. This pr really brings some extra maintain works to shell APIs. I confess that I'm hesitating. But it is the only way I found to solve the synchronous blocking issue. As my individual opinion, I would keep it simple for users.

@scutlight
Copy link
Member Author

An inspiration rushed into my mind when I woke up at midnight 🤪 .
Decomposing the synchronous Shell::Create is such a complicate work, why not just let it be. I make another solution to reduce the main thread blocking to be transient. Please also review that pr #20142 @gaaclarke @liyuqian

@lybeen lybeen mentioned this pull request Aug 12, 2020
8 tasks
@liyuqian
Copy link
Contributor

@scutlight : I guess this is going to be replaced by #20142?

@scutlight
Copy link
Member Author

@liyuqian : Yes. With the changes of #18225 and #20142, creating Shell shall not blocking the main thread for an unacceptable waiting. Close this one.

@scutlight scutlight closed this Sep 1, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
cla: yes perf: speed Performance issues related to (mostly rendering) speed severe: performance Relates to speed or footprint issues.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants