Skip to content

Commit 95f2b72

Browse files
authored
Create root isolate asynchronously (flutter#20142)
## 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 platform thread in order to run `Shell::Setup` synchronously. The cost of `Engine`'s constructor is mainly about the creating of root isolate. Actually, there used to be another time-consuming process, the default font manager setup, which was resolved by flutter/engine#18225. Similar to flutter/engine#18225, this pr move the creation of root isolate out from creating `Engine`. After this action, the main thread blocking is quite an acceptable slice. ## Related Issues flutter#40563 could be resolved by this pr.
1 parent 696c2aa commit 95f2b72

File tree

8 files changed

+149
-56
lines changed

8 files changed

+149
-56
lines changed

runtime/runtime_controller.cc

Lines changed: 93 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,10 @@ namespace flutter {
1818

1919
RuntimeController::RuntimeController(RuntimeDelegate& client,
2020
TaskRunners p_task_runners)
21-
: client_(client), vm_(nullptr), task_runners_(p_task_runners) {}
21+
: client_(client),
22+
vm_(nullptr),
23+
task_runners_(p_task_runners),
24+
weak_factory_(this) {}
2225

2326
RuntimeController::RuntimeController(
2427
RuntimeDelegate& p_client,
@@ -50,48 +53,80 @@ RuntimeController::RuntimeController(
5053
platform_data_(std::move(p_platform_data)),
5154
isolate_create_callback_(p_isolate_create_callback),
5255
isolate_shutdown_callback_(p_isolate_shutdown_callback),
53-
persistent_isolate_data_(std::move(p_persistent_isolate_data)) {
54-
// Create the root isolate as soon as the runtime controller is initialized.
56+
persistent_isolate_data_(std::move(p_persistent_isolate_data)),
57+
weak_factory_(this) {
58+
// Create the root isolate as soon as the runtime controller is initialized,
59+
// but not using a synchronous way to avoid blocking the platform thread a
60+
// long time as it is waiting while creating `Shell` on that platform thread.
5561
// It will be run at a later point when the engine provides a run
5662
// configuration and then runs the isolate.
57-
auto strong_root_isolate =
58-
DartIsolate::CreateRootIsolate(
59-
vm_->GetVMData()->GetSettings(), //
60-
isolate_snapshot_, //
61-
task_runners_, //
62-
std::make_unique<PlatformConfiguration>(this), //
63-
snapshot_delegate_, //
64-
io_manager_, //
65-
unref_queue_, //
66-
image_decoder_, //
67-
p_advisory_script_uri, //
68-
p_advisory_script_entrypoint, //
69-
nullptr, //
70-
isolate_create_callback_, //
71-
isolate_shutdown_callback_ //
72-
)
73-
.lock();
74-
75-
FML_CHECK(strong_root_isolate) << "Could not create root isolate.";
76-
77-
// The root isolate ivar is weak.
78-
root_isolate_ = strong_root_isolate;
79-
80-
strong_root_isolate->SetReturnCodeCallback([this](uint32_t code) {
81-
root_isolate_return_code_ = {true, code};
82-
});
83-
84-
if (auto* platform_configuration = GetPlatformConfigurationIfAvailable()) {
85-
tonic::DartState::Scope scope(strong_root_isolate);
86-
platform_configuration->DidCreateIsolate();
87-
if (!FlushRuntimeStateToIsolate()) {
88-
FML_DLOG(ERROR) << "Could not setup initial isolate state.";
89-
}
90-
} else {
91-
FML_DCHECK(false) << "RuntimeController created without window binding.";
92-
}
93-
94-
FML_DCHECK(Dart_CurrentIsolate() == nullptr);
63+
create_and_config_root_isolate_ =
64+
std::async(std::launch::deferred, [self = weak_factory_.GetWeakPtr()]() {
65+
if (!self) {
66+
return;
67+
}
68+
69+
auto strong_root_isolate =
70+
DartIsolate::CreateRootIsolate(
71+
self->vm_->GetVMData()->GetSettings(), //
72+
self->isolate_snapshot_, //
73+
self->task_runners_, //
74+
std::make_unique<PlatformConfiguration>(self.get()), //
75+
self->snapshot_delegate_, //
76+
self->io_manager_, //
77+
self->unref_queue_, //
78+
self->image_decoder_, //
79+
self->advisory_script_uri_, //
80+
self->advisory_script_entrypoint_, //
81+
nullptr, //
82+
self->isolate_create_callback_, //
83+
self->isolate_shutdown_callback_ //
84+
)
85+
.lock();
86+
87+
FML_CHECK(strong_root_isolate) << "Could not create root isolate.";
88+
89+
// The root isolate ivar is weak.
90+
self->root_isolate_ = strong_root_isolate;
91+
92+
strong_root_isolate->SetReturnCodeCallback([self](uint32_t code) {
93+
if (!self) {
94+
return;
95+
}
96+
97+
self->root_isolate_return_code_ = {true, code};
98+
});
99+
100+
if (auto* platform_configuration =
101+
self->GetPlatformConfigurationIfAvailable()) {
102+
tonic::DartState::Scope scope(strong_root_isolate);
103+
platform_configuration->DidCreateIsolate();
104+
if (!self->FlushRuntimeStateToIsolate()) {
105+
FML_DLOG(ERROR) << "Could not setup initial isolate state.";
106+
}
107+
} else {
108+
FML_DCHECK(false)
109+
<< "RuntimeController created without window binding.";
110+
}
111+
112+
FML_DCHECK(Dart_CurrentIsolate() == nullptr);
113+
114+
self->client_.OnRootIsolateCreated();
115+
return;
116+
});
117+
118+
// We're still trying to create the root isolate as soon as possible here on
119+
// the UI thread although it's deferred a little bit by
120+
// std::async(std::launch::deferred, ...). So the callers of `GetRootIsolate`
121+
// should get a quick return after this UI thread task.
122+
task_runners_.GetUITaskRunner()->PostTask(
123+
[self = weak_factory_.GetWeakPtr()]() {
124+
if (!self) {
125+
return;
126+
}
127+
128+
self->GetRootIsolate();
129+
});
95130
}
96131

97132
RuntimeController::~RuntimeController() {
@@ -107,8 +142,8 @@ RuntimeController::~RuntimeController() {
107142
}
108143
}
109144

110-
bool RuntimeController::IsRootIsolateRunning() const {
111-
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
145+
bool RuntimeController::IsRootIsolateRunning() {
146+
std::shared_ptr<DartIsolate> root_isolate = GetRootIsolate().lock();
112147
if (root_isolate) {
113148
return root_isolate->GetPhase() == DartIsolate::Phase::Running;
114149
}
@@ -234,7 +269,7 @@ bool RuntimeController::ReportTimings(std::vector<int64_t> timings) {
234269
}
235270

236271
bool RuntimeController::NotifyIdle(int64_t deadline) {
237-
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
272+
std::shared_ptr<DartIsolate> root_isolate = GetRootIsolate().lock();
238273
if (!root_isolate) {
239274
return false;
240275
}
@@ -291,7 +326,7 @@ bool RuntimeController::DispatchSemanticsAction(int32_t id,
291326

292327
PlatformConfiguration*
293328
RuntimeController::GetPlatformConfigurationIfAvailable() {
294-
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
329+
std::shared_ptr<DartIsolate> root_isolate = GetRootIsolate().lock();
295330
return root_isolate ? root_isolate->platform_configuration() : nullptr;
296331
}
297332

@@ -353,17 +388,17 @@ RuntimeController::ComputePlatformResolvedLocale(
353388
}
354389

355390
Dart_Port RuntimeController::GetMainPort() {
356-
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
391+
std::shared_ptr<DartIsolate> root_isolate = GetRootIsolate().lock();
357392
return root_isolate ? root_isolate->main_port() : ILLEGAL_PORT;
358393
}
359394

360395
std::string RuntimeController::GetIsolateName() {
361-
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
396+
std::shared_ptr<DartIsolate> root_isolate = GetRootIsolate().lock();
362397
return root_isolate ? root_isolate->debug_name() : "";
363398
}
364399

365400
bool RuntimeController::HasLivePorts() {
366-
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
401+
std::shared_ptr<DartIsolate> root_isolate = GetRootIsolate().lock();
367402
if (!root_isolate) {
368403
return false;
369404
}
@@ -372,11 +407,20 @@ bool RuntimeController::HasLivePorts() {
372407
}
373408

374409
tonic::DartErrorHandleType RuntimeController::GetLastError() {
375-
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
410+
std::shared_ptr<DartIsolate> root_isolate = GetRootIsolate().lock();
376411
return root_isolate ? root_isolate->GetLastError() : tonic::kNoError;
377412
}
378413

379414
std::weak_ptr<DartIsolate> RuntimeController::GetRootIsolate() {
415+
std::shared_ptr<DartIsolate> root_isolate = root_isolate_.lock();
416+
if (root_isolate) {
417+
return root_isolate_;
418+
}
419+
420+
// Root isolate is not yet created, get it and do some configuration.
421+
FML_DCHECK(create_and_config_root_isolate_.valid());
422+
create_and_config_root_isolate_.get();
423+
380424
return root_isolate_;
381425
}
382426

runtime/runtime_controller.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#ifndef FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_
66
#define FLUTTER_RUNTIME_RUNTIME_CONTROLLER_H_
77

8+
#include <future>
89
#include <memory>
910
#include <vector>
1011

@@ -340,7 +341,7 @@ class RuntimeController : public PlatformConfigurationClient {
340341
///
341342
/// @return True if root isolate running, False otherwise.
342343
///
343-
virtual bool IsRootIsolateRunning() const;
344+
virtual bool IsRootIsolateRunning();
344345

345346
//----------------------------------------------------------------------------
346347
/// @brief Dispatch the specified platform message to running root
@@ -422,7 +423,10 @@ class RuntimeController : public PlatformConfigurationClient {
422423
/// @brief Get a weak pointer to the root Dart isolate. This isolate may
423424
/// only be locked on the UI task runner. Callers use this
424425
/// accessor to transition to the root isolate to the running
425-
/// phase.
426+
/// phase. Note that it might take times if the isolate is not yet
427+
/// created, which should be done in a subsequence task after
428+
/// constructing `RuntimeController`, or it should get a quick
429+
/// return otherwise.
426430
///
427431
/// @return The root isolate reference.
428432
///
@@ -471,11 +475,16 @@ class RuntimeController : public PlatformConfigurationClient {
471475
std::string advisory_script_entrypoint_;
472476
std::function<void(int64_t)> idle_notification_callback_;
473477
PlatformData platform_data_;
478+
std::future<void> create_and_config_root_isolate_;
479+
// Note that `root_isolate_` is created asynchronously from the constructor of
480+
// `RuntimeController`, be careful to use it directly while it might have not
481+
// been created yet. Call `GetRootIsolate()` instead which guarantees that.
474482
std::weak_ptr<DartIsolate> root_isolate_;
475483
std::pair<bool, uint32_t> root_isolate_return_code_ = {false, 0};
476484
const fml::closure isolate_create_callback_;
477485
const fml::closure isolate_shutdown_callback_;
478486
std::shared_ptr<const fml::Mapping> persistent_isolate_data_;
487+
fml::WeakPtrFactory<RuntimeController> weak_factory_;
479488

480489
PlatformConfiguration* GetPlatformConfigurationIfAvailable();
481490

runtime/runtime_delegate.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ class RuntimeDelegate {
3232

3333
virtual FontCollection& GetFontCollection() = 0;
3434

35+
virtual void OnRootIsolateCreated() = 0;
36+
3537
virtual void UpdateIsolateDescription(const std::string isolate_name,
3638
int64_t isolate_port) = 0;
3739

shell/common/engine.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -495,6 +495,10 @@ void Engine::HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) {
495495
}
496496
}
497497

498+
void Engine::OnRootIsolateCreated() {
499+
delegate_.OnRootIsolateCreated();
500+
}
501+
498502
void Engine::UpdateIsolateDescription(const std::string isolate_name,
499503
int64_t isolate_port) {
500504
delegate_.UpdateIsolateDescription(isolate_name, isolate_port);

shell/common/engine.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,15 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
185185
///
186186
virtual void OnPreEngineRestart() = 0;
187187

188+
//--------------------------------------------------------------------------
189+
/// @brief Notifies the shell that the root isolate is created.
190+
/// Currently, this information is to add to the service
191+
/// protocol list of available root isolates running in the VM
192+
/// and their names so that the appropriate isolate can be
193+
/// selected in the tools for debugging and instrumentation.
194+
///
195+
virtual void OnRootIsolateCreated() = 0;
196+
188197
//--------------------------------------------------------------------------
189198
/// @brief Notifies the shell of the name of the root isolate and its
190199
/// port when that isolate is launched, restarted (in the
@@ -812,6 +821,9 @@ class Engine final : public RuntimeDelegate, PointerDataDispatcher::Delegate {
812821
// |RuntimeDelegate|
813822
void HandlePlatformMessage(fml::RefPtr<PlatformMessage> message) override;
814823

824+
// |RuntimeDelegate|
825+
void OnRootIsolateCreated() override;
826+
815827
// |RuntimeDelegate|
816828
void UpdateIsolateDescription(const std::string isolate_name,
817829
int64_t isolate_port) override;

shell/common/engine_unittests.cc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ class MockDelegate : public Engine::Delegate {
2424
MOCK_METHOD1(OnEngineHandlePlatformMessage,
2525
void(fml::RefPtr<PlatformMessage>));
2626
MOCK_METHOD0(OnPreEngineRestart, void());
27+
MOCK_METHOD0(OnRootIsolateCreated, void());
2728
MOCK_METHOD2(UpdateIsolateDescription, void(const std::string, int64_t));
2829
MOCK_METHOD1(SetNeedsReportTimings, void(bool));
2930
MOCK_METHOD1(ComputePlatformResolvedLocale,
@@ -46,6 +47,7 @@ class MockRuntimeDelegate : public RuntimeDelegate {
4647
void(SemanticsNodeUpdates, CustomAccessibilityActionUpdates));
4748
MOCK_METHOD1(HandlePlatformMessage, void(fml::RefPtr<PlatformMessage>));
4849
MOCK_METHOD0(GetFontCollection, FontCollection&());
50+
MOCK_METHOD0(OnRootIsolateCreated, void());
4951
MOCK_METHOD2(UpdateIsolateDescription, void(const std::string, int64_t));
5052
MOCK_METHOD1(SetNeedsReportTimings, void(bool));
5153
MOCK_METHOD1(ComputePlatformResolvedLocale,
@@ -57,7 +59,7 @@ class MockRuntimeController : public RuntimeController {
5759
public:
5860
MockRuntimeController(RuntimeDelegate& client, TaskRunners p_task_runners)
5961
: RuntimeController(client, p_task_runners) {}
60-
MOCK_CONST_METHOD0(IsRootIsolateRunning, bool());
62+
MOCK_METHOD0(IsRootIsolateRunning, bool());
6163
MOCK_METHOD1(DispatchPlatformMessage, bool(fml::RefPtr<PlatformMessage>));
6264
};
6365

shell/common/shell.cc

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -562,8 +562,6 @@ bool Shell::Setup(std::unique_ptr<PlatformView> platform_view,
562562

563563
is_setup_ = true;
564564

565-
vm_->GetServiceProtocol()->AddHandler(this, GetServiceProtocolDescription());
566-
567565
PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner(
568566
task_runners_.GetIOTaskRunner());
569567

@@ -1133,6 +1131,19 @@ void Shell::OnPreEngineRestart() {
11331131
latch.Wait();
11341132
}
11351133

1134+
// |Engine::Delegate|
1135+
void Shell::OnRootIsolateCreated() {
1136+
auto description = GetServiceProtocolDescription();
1137+
fml::TaskRunner::RunNowOrPostTask(
1138+
task_runners_.GetPlatformTaskRunner(),
1139+
[self = weak_factory_.GetWeakPtr(),
1140+
description = std::move(description)]() {
1141+
if (self) {
1142+
self->vm_->GetServiceProtocol()->AddHandler(self.get(), description);
1143+
}
1144+
});
1145+
}
1146+
11361147
// |Engine::Delegate|
11371148
void Shell::UpdateIsolateDescription(const std::string isolate_name,
11381149
int64_t isolate_port) {
@@ -1277,9 +1288,15 @@ bool Shell::HandleServiceProtocolMessage(
12771288
// |ServiceProtocol::Handler|
12781289
ServiceProtocol::Handler::Description Shell::GetServiceProtocolDescription()
12791290
const {
1291+
FML_DCHECK(task_runners_.GetUITaskRunner()->RunsTasksOnCurrentThread());
1292+
1293+
if (!weak_engine_) {
1294+
return ServiceProtocol::Handler::Description();
1295+
}
1296+
12801297
return {
1281-
engine_->GetUIIsolateMainPort(),
1282-
engine_->GetUIIsolateName(),
1298+
weak_engine_->GetUIIsolateMainPort(),
1299+
weak_engine_->GetUIIsolateName(),
12831300
};
12841301
}
12851302

shell/common/shell.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,9 @@ class Shell final : public PlatformView::Delegate,
513513
// |Engine::Delegate|
514514
void OnPreEngineRestart() override;
515515

516+
// |Engine::Delegate|
517+
void OnRootIsolateCreated() override;
518+
516519
// |Engine::Delegate|
517520
void UpdateIsolateDescription(const std::string isolate_name,
518521
int64_t isolate_port) override;

0 commit comments

Comments
 (0)