Isolates launched by the engine instance use the settings of that instance. (#22052)
This regression was introduced in https://github.com/flutter/engine/pull/21820
for sound-null safety. The settings used to launch the VM were incorrectly used
to determine the isolate lifecycle callbacks. Since the first shell/engine in
the process also starts the VM, these objects are usually identical. However,
for subsequent engine shell/engine launches, the callbacks attached to the new
settings object would be ignored. The unit-test harness is also structured in
such a way that each test case tears down the VM before the next. So all
existing tests created a bespoke VM for the test run, and, the tests that did
create multiple isolates did not also test attaching callbacks to the settings
object.
Fixes https://github.com/flutter/engine/pull/22041
diff --git a/runtime/dart_isolate_unittests.cc b/runtime/dart_isolate_unittests.cc
index f6263dd..a90599f 100644
--- a/runtime/dart_isolate_unittests.cc
+++ b/runtime/dart_isolate_unittests.cc
@@ -402,5 +402,33 @@
ASSERT_EQ(create_callback_count, 1u);
}
+TEST_F(DartIsolateTest,
+ IsolateCreateCallbacksTakeInstanceSettingsInsteadOfVMSettings) {
+ ASSERT_FALSE(DartVMRef::IsInstanceRunning());
+ auto vm_settings = CreateSettingsForFixture();
+ auto vm_ref = DartVMRef::Create(vm_settings);
+ auto instance_settings = vm_settings;
+ size_t create_callback_count = 0u;
+ instance_settings.root_isolate_create_callback =
+ [&create_callback_count](const auto& isolate) {
+ ASSERT_EQ(isolate.GetPhase(), DartIsolate::Phase::Ready);
+ create_callback_count++;
+ ASSERT_NE(::Dart_CurrentIsolate(), nullptr);
+ };
+ TaskRunners task_runners(GetCurrentTestName(), //
+ GetCurrentTaskRunner(), //
+ GetCurrentTaskRunner(), //
+ GetCurrentTaskRunner(), //
+ GetCurrentTaskRunner() //
+ );
+ {
+ auto isolate = RunDartCodeInIsolate(vm_ref, instance_settings, task_runners,
+ "main", {}, GetFixturesPath());
+ ASSERT_TRUE(isolate);
+ ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running);
+ }
+ ASSERT_EQ(create_callback_count, 1u);
+}
+
} // namespace testing
} // namespace flutter
diff --git a/runtime/runtime_controller.cc b/runtime/runtime_controller.cc
index a2ea4a0..83d1292 100644
--- a/runtime/runtime_controller.cc
+++ b/runtime/runtime_controller.cc
@@ -342,6 +342,7 @@
}
bool RuntimeController::LaunchRootIsolate(
+ const Settings& settings,
std::optional<std::string> dart_entrypoint,
std::optional<std::string> dart_entrypoint_library,
std::unique_ptr<IsolateConfiguration> isolate_configuration) {
@@ -352,7 +353,7 @@
auto strong_root_isolate =
DartIsolate::CreateRunningRootIsolate(
- vm_->GetVMData()->GetSettings(), //
+ settings, //
isolate_snapshot_, //
task_runners_, //
std::make_unique<PlatformConfiguration>(this), //
diff --git a/runtime/runtime_controller.h b/runtime/runtime_controller.h
index 9b5ef99..17b02b9 100644
--- a/runtime/runtime_controller.h
+++ b/runtime/runtime_controller.h
@@ -144,6 +144,7 @@
/// runtime controller, `Clone` this runtime controller and
/// Launch an isolate in that runtime controller instead.
///
+ /// @param[in] settings The per engine instance settings.
/// @param[in] dart_entrypoint The dart entrypoint. If
/// `std::nullopt` or empty, `main` will
/// be attempted.
@@ -156,6 +157,7 @@
/// `DartIsolate::Phase::Running` phase.
///
[[nodiscard]] bool LaunchRootIsolate(
+ const Settings& settings,
std::optional<std::string> dart_entrypoint,
std::optional<std::string> dart_entrypoint_library,
std::unique_ptr<IsolateConfiguration> isolate_configuration);
diff --git a/shell/common/engine.cc b/shell/common/engine.cc
index f3374a0..6c9304f 100644
--- a/shell/common/engine.cc
+++ b/shell/common/engine.cc
@@ -160,6 +160,7 @@
}
if (!runtime_controller_->LaunchRootIsolate(
+ settings_, //
configuration.GetEntrypoint(), //
configuration.GetEntrypointLibrary(), //
configuration.TakeIsolateConfiguration()) //
diff --git a/shell/common/shell_unittests.cc b/shell/common/shell_unittests.cc
index d483773..e15182e 100644
--- a/shell/common/shell_unittests.cc
+++ b/shell/common/shell_unittests.cc
@@ -2185,5 +2185,28 @@
DestroyShell(std::move(shell));
}
+TEST_F(ShellTest, EngineRootIsolateLaunchesDontTakeVMDataSettings) {
+ ASSERT_FALSE(DartVMRef::IsInstanceRunning());
+ // Make sure the shell launch does not kick off the creation of the VM
+ // instance by already creating one upfront.
+ auto vm_settings = CreateSettingsForFixture();
+ auto vm_ref = DartVMRef::Create(vm_settings);
+ ASSERT_TRUE(DartVMRef::IsInstanceRunning());
+
+ auto settings = vm_settings;
+ fml::AutoResetWaitableEvent isolate_create_latch;
+ settings.root_isolate_create_callback = [&](const auto& isolate) {
+ isolate_create_latch.Signal();
+ };
+ auto shell = CreateShell(settings);
+ ASSERT_TRUE(ValidateShell(shell.get()));
+ auto configuration = RunConfiguration::InferFromSettings(settings);
+ ASSERT_TRUE(configuration.IsValid());
+ RunEngine(shell.get(), std::move(configuration));
+ ASSERT_TRUE(DartVMRef::IsInstanceRunning());
+ DestroyShell(std::move(shell));
+ isolate_create_latch.Wait();
+}
+
} // namespace testing
} // namespace flutter