From 75a66f31dc2a02ccb54f10fb4477233086f4906b Mon Sep 17 00:00:00 2001 From: Chinmay Garde Date: Fri, 15 Feb 2019 14:16:17 -0800 Subject: [PATCH] Shut down and restart the Dart VM as needed. (flutter/engine#7832) The shell was already designed to cleanly shut down the VM but it couldnt earlier as |Dart_Initialize| could never be called after a |Dart_Cleanup|. This meant that shutting down an engine instance could not shut down the VM to save memory because newly created engines in the process after that point couldn't restart the VM. There can only be one VM running in a process at a time. This patch separate the previous DartVM object into one that references a running instance of the DartVM and a set of immutable dependencies that components can reference even as the VM is shutting down. Unit tests have been added to assert that non-overlapping engine launches use difference VM instances. --- .../ci/licenses_golden/licenses_flutter | 4 + .../isolate_name_server_natives.cc | 18 +- engine/src/flutter/lib/ui/ui_dart_state.cc | 6 +- engine/src/flutter/lib/ui/ui_dart_state.h | 6 +- engine/src/flutter/runtime/BUILD.gn | 9 +- engine/src/flutter/runtime/dart_isolate.cc | 100 +++++------ engine/src/flutter/runtime/dart_isolate.h | 26 +-- .../flutter/runtime/dart_isolate_unittests.cc | 115 +++++++------ engine/src/flutter/runtime/dart_vm.cc | 162 ++++++++---------- engine/src/flutter/runtime/dart_vm.h | 56 +++--- engine/src/flutter/runtime/dart_vm_data.cc | 79 +++++++++ engine/src/flutter/runtime/dart_vm_data.h | 47 +++++ .../src/flutter/runtime/dart_vm_lifecycle.cc | 112 ++++++++++++ .../src/flutter/runtime/dart_vm_lifecycle.h | 72 ++++++++ .../src/flutter/runtime/dart_vm_unittests.cc | 104 ++++++++++- .../src/flutter/runtime/runtime_controller.cc | 10 +- .../src/flutter/runtime/runtime_controller.h | 12 +- engine/src/flutter/shell/common/engine.cc | 4 +- engine/src/flutter/shell/common/engine.h | 4 +- engine/src/flutter/shell/common/shell.cc | 88 ++++++---- engine/src/flutter/shell/common/shell.h | 21 ++- engine/src/flutter/testing/testing.cc | 4 +- engine/src/flutter/testing/testing.h | 4 + 23 files changed, 742 insertions(+), 321 deletions(-) create mode 100644 engine/src/flutter/runtime/dart_vm_data.cc create mode 100644 engine/src/flutter/runtime/dart_vm_data.h create mode 100644 engine/src/flutter/runtime/dart_vm_lifecycle.cc create mode 100644 engine/src/flutter/runtime/dart_vm_lifecycle.h diff --git a/engine/src/flutter/ci/licenses_golden/licenses_flutter b/engine/src/flutter/ci/licenses_golden/licenses_flutter index 8696116e1a..718c74a1c0 100644 --- a/engine/src/flutter/ci/licenses_golden/licenses_flutter +++ b/engine/src/flutter/ci/licenses_golden/licenses_flutter @@ -355,6 +355,10 @@ FILE: ../../../flutter/runtime/dart_snapshot_buffer.cc FILE: ../../../flutter/runtime/dart_snapshot_buffer.h FILE: ../../../flutter/runtime/dart_vm.cc FILE: ../../../flutter/runtime/dart_vm.h +FILE: ../../../flutter/runtime/dart_vm_data.cc +FILE: ../../../flutter/runtime/dart_vm_data.h +FILE: ../../../flutter/runtime/dart_vm_lifecycle.cc +FILE: ../../../flutter/runtime/dart_vm_lifecycle.h FILE: ../../../flutter/runtime/dart_vm_unittests.cc FILE: ../../../flutter/runtime/embedder_resources.cc FILE: ../../../flutter/runtime/embedder_resources.h diff --git a/engine/src/flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc b/engine/src/flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc index 7f4467886f..3f7f80ef05 100644 --- a/engine/src/flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc +++ b/engine/src/flutter/lib/ui/isolate_name_server/isolate_name_server_natives.cc @@ -14,8 +14,10 @@ namespace blink { Dart_Handle IsolateNameServerNatives::LookupPortByName( const std::string& name) { - IsolateNameServer* name_server = - UIDartState::Current()->GetIsolateNameServer(); + auto name_server = UIDartState::Current()->GetIsolateNameServer(); + if (!name_server) { + return Dart_Null(); + } Dart_Port port = name_server->LookupIsolatePortByName(name); if (port == ILLEGAL_PORT) { return Dart_Null(); @@ -26,8 +28,10 @@ Dart_Handle IsolateNameServerNatives::LookupPortByName( Dart_Handle IsolateNameServerNatives::RegisterPortWithName( Dart_Handle port_handle, const std::string& name) { - IsolateNameServer* name_server = - UIDartState::Current()->GetIsolateNameServer(); + auto name_server = UIDartState::Current()->GetIsolateNameServer(); + if (!name_server) { + return Dart_False(); + } Dart_Port port = ILLEGAL_PORT; Dart_SendPortGetId(port_handle, &port); if (!name_server->RegisterIsolatePortWithName(port, name)) { @@ -38,8 +42,10 @@ Dart_Handle IsolateNameServerNatives::RegisterPortWithName( Dart_Handle IsolateNameServerNatives::RemovePortNameMapping( const std::string& name) { - IsolateNameServer* name_server = - UIDartState::Current()->GetIsolateNameServer(); + auto name_server = UIDartState::Current()->GetIsolateNameServer(); + if (!name_server) { + return Dart_False(); + } if (!name_server->RemoveIsolateNameMapping(name)) { return Dart_False(); } diff --git a/engine/src/flutter/lib/ui/ui_dart_state.cc b/engine/src/flutter/lib/ui/ui_dart_state.cc index b42cbaf5ba..be0a5348f3 100644 --- a/engine/src/flutter/lib/ui/ui_dart_state.cc +++ b/engine/src/flutter/lib/ui/ui_dart_state.cc @@ -23,7 +23,7 @@ UIDartState::UIDartState( std::string advisory_script_entrypoint, std::string logger_prefix, UnhandledExceptionCallback unhandled_exception_callback, - IsolateNameServer* isolate_name_server) + std::shared_ptr isolate_name_server) : task_runners_(std::move(task_runners)), add_callback_(std::move(add_callback)), remove_callback_(std::move(remove_callback)), @@ -33,7 +33,7 @@ UIDartState::UIDartState( advisory_script_entrypoint_(std::move(advisory_script_entrypoint)), logger_prefix_(std::move(logger_prefix)), unhandled_exception_callback_(unhandled_exception_callback), - isolate_name_server_(isolate_name_server) { + isolate_name_server_(std::move(isolate_name_server)) { AddOrRemoveTaskObserver(true /* add */); } @@ -124,7 +124,7 @@ fml::WeakPtr UIDartState::GetResourceContext() const { return io_manager_->GetResourceContext(); } -IsolateNameServer* UIDartState::GetIsolateNameServer() { +std::shared_ptr UIDartState::GetIsolateNameServer() const { return isolate_name_server_; } diff --git a/engine/src/flutter/lib/ui/ui_dart_state.h b/engine/src/flutter/lib/ui/ui_dart_state.h index f2c63cbace..afa5e47e83 100644 --- a/engine/src/flutter/lib/ui/ui_dart_state.h +++ b/engine/src/flutter/lib/ui/ui_dart_state.h @@ -53,7 +53,7 @@ class UIDartState : public tonic::DartState { fml::WeakPtr GetResourceContext() const; - IsolateNameServer* GetIsolateNameServer(); + std::shared_ptr GetIsolateNameServer() const; tonic::DartErrorHandleType GetLastError(); @@ -81,7 +81,7 @@ class UIDartState : public tonic::DartState { std::string advisory_script_entrypoint, std::string logger_prefix, UnhandledExceptionCallback unhandled_exception_callback, - IsolateNameServer* isolate_name_server); + std::shared_ptr isolate_name_server); ~UIDartState() override; @@ -107,7 +107,7 @@ class UIDartState : public tonic::DartState { std::unique_ptr window_; tonic::DartMicrotaskQueue microtask_queue_; UnhandledExceptionCallback unhandled_exception_callback_; - IsolateNameServer* isolate_name_server_; + const std::shared_ptr isolate_name_server_; void AddOrRemoveTaskObserver(bool add); }; diff --git a/engine/src/flutter/runtime/BUILD.gn b/engine/src/flutter/runtime/BUILD.gn index 5df979ed68..5619851dfe 100644 --- a/engine/src/flutter/runtime/BUILD.gn +++ b/engine/src/flutter/runtime/BUILD.gn @@ -36,6 +36,10 @@ source_set("runtime") { "dart_snapshot_buffer.h", "dart_vm.cc", "dart_vm.h", + "dart_vm_data.cc", + "dart_vm_data.h", + "dart_vm_lifecycle.cc", + "dart_vm_lifecycle.h", "embedder_resources.cc", "embedder_resources.h", "runtime_controller.cc", @@ -73,7 +77,9 @@ source_set("runtime") { flutter_runtime_mode != "dynamic_release" && !is_fuchsia) { # Only link in Observatory in non-release modes on non-Fuchsia. Fuchsia # instead puts Observatory into the runner's package. - deps += [ "//third_party/dart/runtime/observatory:embedded_observatory_archive" ] + deps += [ + "//third_party/dart/runtime/observatory:embedded_observatory_archive", + ] } } @@ -95,6 +101,7 @@ executable("runtime_unittests") { ":runtime_fixtures", "$flutter_root/fml", "$flutter_root/lib/snapshot", + "$flutter_root/shell/common", "$flutter_root/testing", "//third_party/dart/runtime:libdart_jit", "//third_party/skia", diff --git a/engine/src/flutter/runtime/dart_isolate.cc b/engine/src/flutter/runtime/dart_isolate.cc index 4340ccdc39..77e55afd69 100644 --- a/engine/src/flutter/runtime/dart_isolate.cc +++ b/engine/src/flutter/runtime/dart_isolate.cc @@ -14,6 +14,7 @@ #include "flutter/lib/ui/dart_ui.h" #include "flutter/runtime/dart_service_isolate.h" #include "flutter/runtime/dart_vm.h" +#include "flutter/runtime/dart_vm_lifecycle.h" #include "third_party/dart/runtime/include/dart_api.h" #include "third_party/dart/runtime/include/dart_tools_api.h" #include "third_party/tonic/converter/dart_converter.h" @@ -29,9 +30,9 @@ namespace blink { std::weak_ptr DartIsolate::CreateRootIsolate( - DartVM* vm, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + const Settings& settings, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, TaskRunners task_runners, std::unique_ptr window, fml::WeakPtr snapshot_delegate, @@ -50,7 +51,7 @@ std::weak_ptr DartIsolate::CreateRootIsolate( // isolate lifecycle is entirely managed by the VM). auto root_embedder_data = std::make_unique>( std::make_shared( - vm, // VM + settings, // settings std::move(isolate_snapshot), // isolate snapshot std::move(shared_snapshot), // shared snapshot task_runners, // task runners @@ -93,9 +94,9 @@ std::weak_ptr DartIsolate::CreateRootIsolate( return embedder_isolate; } -DartIsolate::DartIsolate(DartVM* vm, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, +DartIsolate::DartIsolate(const Settings& settings, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, TaskRunners task_runners, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, @@ -103,36 +104,31 @@ DartIsolate::DartIsolate(DartVM* vm, std::string advisory_script_entrypoint, ChildIsolatePreparer child_isolate_preparer) : UIDartState(std::move(task_runners), - vm->GetSettings().task_observer_add, - vm->GetSettings().task_observer_remove, + settings.task_observer_add, + settings.task_observer_remove, std::move(snapshot_delegate), std::move(io_manager), advisory_script_uri, advisory_script_entrypoint, - vm->GetSettings().log_tag, - vm->GetSettings().unhandled_exception_callback, - vm->GetIsolateNameServer()), - vm_(vm), + settings.log_tag, + settings.unhandled_exception_callback, + DartVMRef::GetIsolateNameServer()), + settings_(settings), isolate_snapshot_(std::move(isolate_snapshot)), shared_snapshot_(std::move(shared_snapshot)), child_isolate_preparer_(std::move(child_isolate_preparer)) { FML_DCHECK(isolate_snapshot_) << "Must contain a valid isolate snapshot."; - - if (vm_ == nullptr) { - return; - } - phase_ = Phase::Uninitialized; } DartIsolate::~DartIsolate() = default; -DartIsolate::Phase DartIsolate::GetPhase() const { - return phase_; +const Settings& DartIsolate::GetSettings() const { + return settings_; } -DartVM* DartIsolate::GetDartVM() const { - return vm_; +DartIsolate::Phase DartIsolate::GetPhase() const { + return phase_; } bool DartIsolate::Initialize(Dart_Isolate dart_isolate, bool is_root_isolate) { @@ -493,16 +489,16 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( const char* package_config, Dart_IsolateFlags* flags, char** error) { - auto vm = DartVM::ForProcessIfInitialized(); + auto vm_data = DartVMRef::GetVMData(); - if (!vm) { + if (!vm_data) { *error = strdup( - "Could not resolve the VM when attempting to create the service " - "isolate."); + "Could not access VM data to initialize isolates. This may be because " + "the VM has initialized shutdown on another thread already."); return nullptr; } - const auto& settings = vm->GetSettings(); + const auto& settings = vm_data->GetSettings(); if (!settings.enable_observatory) { FML_DLOG(INFO) << "Observatory is disabled."; @@ -515,21 +511,26 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( flags->load_vmservice_library = true; + if (advisory_script_uri == nullptr) { + advisory_script_uri = ""; + } + + if (advisory_script_entrypoint == nullptr) { + advisory_script_entrypoint = ""; + } + std::weak_ptr weak_service_isolate = DartIsolate::CreateRootIsolate( - vm.get(), // vm - vm->GetIsolateSnapshot(), // isolate snapshot - vm->GetSharedSnapshot(), // shared snapshot - null_task_runners, // task runners - nullptr, // window - {}, // snapshot delegate - {}, // IO Manager - advisory_script_uri == nullptr ? "" - : advisory_script_uri, // script uri - advisory_script_entrypoint == nullptr - ? "" - : advisory_script_entrypoint, // script entrypoint - flags // flags + vm_data->GetSettings(), // settings + vm_data->GetIsolateSnapshot(), // service isolate snapshot + vm_data->GetSharedSnapshot(), // shared snapshot + null_task_runners, // service isolate task runners + nullptr, // window + {}, // snapshot delegate + {}, // IO Manager + advisory_script_uri, // script uri + advisory_script_entrypoint, // script entrypoint + flags // flags ); std::shared_ptr service_isolate = weak_service_isolate.lock(); @@ -552,7 +553,13 @@ Dart_Isolate DartIsolate::DartCreateAndStartServiceIsolate( return nullptr; } - vm->GetServiceProtocol().ToggleHooks(true); + if (auto service_protocol = DartVMRef::GetServiceProtocol()) { + service_protocol->ToggleHooks(true); + } else { + FML_DLOG(ERROR) + << "Could not acquire the service protocol handlers. This might be " + "because the VM has already begun teardown on another thread."; + } return service_isolate->isolate(); } @@ -610,16 +617,13 @@ DartIsolate::CreateDartVMAndEmbedderObjectPair( std::unique_ptr> embedder_isolate( p_parent_embedder_isolate); - if (embedder_isolate == nullptr || - (*embedder_isolate)->GetDartVM() == nullptr) { + if (embedder_isolate == nullptr) { *error = strdup("Parent isolate did not have embedder specific callback data."); FML_DLOG(ERROR) << *error; return {nullptr, {}}; } - DartVM* const vm = (*embedder_isolate)->GetDartVM(); - if (!is_root_isolate) { auto* raw_embedder_isolate = embedder_isolate.release(); @@ -628,7 +632,7 @@ DartIsolate::CreateDartVMAndEmbedderObjectPair( embedder_isolate = std::make_unique>( std::make_shared( - vm, // vm + (*raw_embedder_isolate)->GetSettings(), // settings (*raw_embedder_isolate)->GetIsolateSnapshot(), // isolate_snapshot (*raw_embedder_isolate)->GetSharedSnapshot(), // shared_snapshot null_task_runners, // task_runners @@ -701,11 +705,11 @@ void DartIsolate::DartIsolateCleanupCallback( delete embedder_isolate; } -fml::RefPtr DartIsolate::GetIsolateSnapshot() const { +fml::RefPtr DartIsolate::GetIsolateSnapshot() const { return isolate_snapshot_; } -fml::RefPtr DartIsolate::GetSharedSnapshot() const { +fml::RefPtr DartIsolate::GetSharedSnapshot() const { return shared_snapshot_; } diff --git a/engine/src/flutter/runtime/dart_isolate.h b/engine/src/flutter/runtime/dart_isolate.h index bcd90a80d1..31a0ca9c99 100644 --- a/engine/src/flutter/runtime/dart_isolate.h +++ b/engine/src/flutter/runtime/dart_isolate.h @@ -41,9 +41,9 @@ class DartIsolate : public UIDartState { // bindings. From the VM's perspective, this isolate is not special in any // way. static std::weak_ptr CreateRootIsolate( - DartVM* vm, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + const Settings& settings, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, TaskRunners task_runners, std::unique_ptr window, fml::WeakPtr snapshot_delegate, @@ -52,9 +52,9 @@ class DartIsolate : public UIDartState { std::string advisory_script_entrypoint, Dart_IsolateFlags* flags = nullptr); - DartIsolate(DartVM* vm, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + DartIsolate(const Settings& settings, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, TaskRunners task_runners, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, @@ -64,6 +64,8 @@ class DartIsolate : public UIDartState { ~DartIsolate() override; + const Settings& GetSettings() const; + Phase GetPhase() const; FML_WARN_UNUSED_RESULT @@ -85,11 +87,9 @@ class DartIsolate : public UIDartState { void AddIsolateShutdownCallback(fml::closure closure); - DartVM* GetDartVM() const; + fml::RefPtr GetIsolateSnapshot() const; - fml::RefPtr GetIsolateSnapshot() const; - - fml::RefPtr GetSharedSnapshot() const; + fml::RefPtr GetSharedSnapshot() const; std::weak_ptr GetWeakIsolatePtr(); @@ -108,10 +108,10 @@ class DartIsolate : public UIDartState { }; friend class DartVM; - DartVM* const vm_ = nullptr; Phase phase_ = Phase::Unknown; - const fml::RefPtr isolate_snapshot_; - const fml::RefPtr shared_snapshot_; + const Settings settings_; + const fml::RefPtr isolate_snapshot_; + const fml::RefPtr shared_snapshot_; std::vector> kernel_buffers_; std::vector> shutdown_callbacks_; ChildIsolatePreparer child_isolate_preparer_ = nullptr; diff --git a/engine/src/flutter/runtime/dart_isolate_unittests.cc b/engine/src/flutter/runtime/dart_isolate_unittests.cc index ab6c29617e..1ebb335be0 100644 --- a/engine/src/flutter/runtime/dart_isolate_unittests.cc +++ b/engine/src/flutter/runtime/dart_isolate_unittests.cc @@ -7,15 +7,11 @@ #include "flutter/fml/thread.h" #include "flutter/runtime/dart_isolate.h" #include "flutter/runtime/dart_vm.h" +#include "flutter/runtime/dart_vm_lifecycle.h" #include "flutter/testing/testing.h" #include "flutter/testing/thread_test.h" #include "third_party/tonic/scopes/dart_isolate_scope.h" -#define CURRENT_TEST_NAME \ - std::string { \ - ::testing::UnitTest::GetInstance()->current_test_info()->name() \ - } - namespace blink { using DartIsolateTest = ::testing::ThreadTest; @@ -24,24 +20,26 @@ TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) { Settings settings = {}; settings.task_observer_add = [](intptr_t, fml::closure) {}; settings.task_observer_remove = [](intptr_t) {}; - auto vm = DartVM::ForProcess(settings); + auto vm = DartVMRef::Create(settings); ASSERT_TRUE(vm); - TaskRunners task_runners(CURRENT_TEST_NAME, // - GetCurrentTaskRunner(), // - GetCurrentTaskRunner(), // - GetCurrentTaskRunner(), // - GetCurrentTaskRunner() // + auto vm_data = vm->GetVMData(); + ASSERT_TRUE(vm_data); + TaskRunners task_runners(testing::GetCurrentTestName(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner() // ); auto weak_isolate = DartIsolate::CreateRootIsolate( - vm.get(), // vm - vm->GetIsolateSnapshot(), // isolate snapshot - vm->GetSharedSnapshot(), // shared snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // io manager - "main.dart", // advisory uri - "main" // advisory entrypoint + vm_data->GetSettings(), // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + vm_data->GetSharedSnapshot(), // shared snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // io manager + "main.dart", // advisory uri + "main" // advisory entrypoint ); auto root_isolate = weak_isolate.lock(); ASSERT_TRUE(root_isolate); @@ -53,24 +51,26 @@ TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) { Settings settings = {}; settings.task_observer_add = [](intptr_t, fml::closure) {}; settings.task_observer_remove = [](intptr_t) {}; - auto vm = DartVM::ForProcess(settings); + auto vm = DartVMRef::Create(settings); ASSERT_TRUE(vm); - TaskRunners task_runners(CURRENT_TEST_NAME, // - GetCurrentTaskRunner(), // - GetCurrentTaskRunner(), // - GetCurrentTaskRunner(), // - GetCurrentTaskRunner() // + auto vm_data = vm->GetVMData(); + ASSERT_TRUE(vm_data); + TaskRunners task_runners(testing::GetCurrentTestName(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner(), // + GetCurrentTaskRunner() // ); auto weak_isolate = DartIsolate::CreateRootIsolate( - vm.get(), // vm - vm->GetIsolateSnapshot(), // isolate snapshot - vm->GetSharedSnapshot(), // shared snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // io manager - "main.dart", // advisory uri - "main" // advisory entrypoint + vm_data->GetSettings(), // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + vm_data->GetSharedSnapshot(), // shared snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // io manager + "main.dart", // advisory uri + "main" // advisory entrypoint ); auto root_isolate = weak_isolate.lock(); ASSERT_TRUE(root_isolate); @@ -86,8 +86,11 @@ TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) { class AutoIsolateShutdown { public: - AutoIsolateShutdown(std::shared_ptr isolate) - : isolate_(std::move(isolate)) {} + AutoIsolateShutdown(blink::DartVMRef vm, + std::shared_ptr isolate) + : vm_(std::move(vm)), isolate_(std::move(isolate)) { + FML_CHECK(vm_); + } ~AutoIsolateShutdown() { if (isolate_) { @@ -119,6 +122,7 @@ class AutoIsolateShutdown { } private: + blink::DartVMRef vm_; std::shared_ptr isolate_; FML_DISALLOW_COPY_AND_ASSIGN(AutoIsolateShutdown); @@ -128,35 +132,38 @@ std::unique_ptr RunDartCodeInIsolate( fml::RefPtr task_runner, std::string entrypoint) { Settings settings = {}; + settings.enable_observatory = true; settings.task_observer_add = [](intptr_t, fml::closure) {}; settings.task_observer_remove = [](intptr_t) {}; - auto vm = DartVM::ForProcess(settings); - + auto vm = DartVMRef::Create(settings); if (!vm) { return {}; } - TaskRunners task_runners(CURRENT_TEST_NAME, // - task_runner, // - task_runner, // - task_runner, // - task_runner // + + auto vm_data = vm->GetVMData(); + + TaskRunners task_runners(testing::GetCurrentTestName(), // + task_runner, // + task_runner, // + task_runner, // + task_runner // ); auto weak_isolate = DartIsolate::CreateRootIsolate( - vm.get(), // vm - vm->GetIsolateSnapshot(), // isolate snapshot - vm->GetSharedSnapshot(), // shared snapshot - std::move(task_runners), // task runners - nullptr, // window - {}, // snapshot delegate - {}, // io manager - "main.dart", // advisory uri - "main" // advisory entrypoint + vm_data->GetSettings(), // settings + vm_data->GetIsolateSnapshot(), // isolate snapshot + vm_data->GetSharedSnapshot(), // shared snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // io manager + "main.dart", // advisory uri + "main" // advisory entrypoint ); auto root_isolate = - std::make_unique(weak_isolate.lock()); + std::make_unique(std::move(vm), weak_isolate.lock()); if (!root_isolate->IsValid()) { FML_LOG(ERROR) << "Could not create isolate."; diff --git a/engine/src/flutter/runtime/dart_vm.cc b/engine/src/flutter/runtime/dart_vm.cc index 382ab05c34..fd75c3039e 100644 --- a/engine/src/flutter/runtime/dart_vm.cc +++ b/engine/src/flutter/runtime/dart_vm.cc @@ -15,6 +15,7 @@ #include "flutter/fml/file.h" #include "flutter/fml/logging.h" #include "flutter/fml/mapping.h" +#include "flutter/fml/synchronization/thread_annotations.h" #include "flutter/fml/time/time_delta.h" #include "flutter/fml/trace_event.h" #include "flutter/lib/io/dart_io.h" @@ -239,66 +240,48 @@ static void EmbedderInformationCallback(Dart_EmbedderInformation* info) { info->name = "Flutter"; } -fml::RefPtr DartVM::ForProcess(Settings settings) { - return ForProcess(settings, nullptr, nullptr, nullptr); -} - -static std::once_flag gVMInitialization; -static std::mutex gVMMutex; -static fml::RefPtr gVM; - -fml::RefPtr DartVM::ForProcess( +std::shared_ptr DartVM::Create( Settings settings, fml::RefPtr vm_snapshot, fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot) { - std::lock_guard lock(gVMMutex); - std::call_once(gVMInitialization, [settings, // - vm_snapshot, // - isolate_snapshot, // - shared_snapshot // - ]() mutable { - if (!vm_snapshot) { - vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings); - } - if (!(vm_snapshot && vm_snapshot->IsValid())) { - FML_LOG(ERROR) << "VM snapshot must be valid."; - return; - } - if (!isolate_snapshot) { - isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings); - } - if (!(isolate_snapshot && isolate_snapshot->IsValid())) { - FML_LOG(ERROR) << "Isolate snapshot must be valid."; - return; - } - if (!shared_snapshot) { - shared_snapshot = DartSnapshot::Empty(); - } - gVM = fml::MakeRefCounted(settings, // - std::move(vm_snapshot), // - std::move(isolate_snapshot), // - std::move(shared_snapshot) // - ); - }); - return gVM; + fml::RefPtr shared_snapshot, + std::shared_ptr isolate_name_server) { + auto vm_data = DartVMData::Create(settings, // + std::move(vm_snapshot), // + std::move(isolate_snapshot), // + std::move(shared_snapshot) // + ); + + if (!vm_data) { + FML_LOG(ERROR) << "Could not setup VM data to bootstrap the VM from."; + return {}; + } + + // Note: std::make_shared unviable due to hidden constructor. + return std::shared_ptr( + new DartVM(std::move(vm_data), std::move(isolate_name_server))); } -fml::RefPtr DartVM::ForProcessIfInitialized() { - std::lock_guard lock(gVMMutex); - return gVM; +static std::atomic_size_t gVMLaunchCount; + +size_t DartVM::GetVMLaunchCount() { + return gVMLaunchCount; } -DartVM::DartVM(const Settings& settings, - fml::RefPtr vm_snapshot, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot) - : settings_(settings), - vm_snapshot_(std::move(vm_snapshot)), - isolate_snapshot_(std::move(isolate_snapshot)), - shared_snapshot_(std::move(shared_snapshot)), - weak_factory_(this) { +DartVM::DartVM(std::shared_ptr vm_data, + std::shared_ptr isolate_name_server) + : settings_(vm_data->GetSettings()), + vm_data_(vm_data), + isolate_name_server_(std::move(isolate_name_server)), + service_protocol_(std::make_shared()) { TRACE_EVENT0("flutter", "DartVMInitializer"); + + gVMLaunchCount++; + + FML_DCHECK(vm_data_); + FML_DCHECK(isolate_name_server_); + FML_DCHECK(service_protocol_); + FML_DLOG(INFO) << "Attempting Dart VM launch for mode: " << (IsRunningPrecompiledCode() ? "AOT" : "Interpreter"); @@ -306,8 +289,8 @@ DartVM::DartVM(const Settings& settings, TRACE_EVENT0("flutter", "dart::bin::BootstrapDartIo"); dart::bin::BootstrapDartIo(); - if (!settings.temp_directory_path.empty()) { - dart::bin::SetSystemTempDirectory(settings.temp_directory_path.c_str()); + if (!settings_.temp_directory_path.empty()) { + dart::bin::SetSystemTempDirectory(settings_.temp_directory_path.c_str()); } } @@ -320,7 +303,7 @@ DartVM::DartVM(const Settings& settings, args.push_back("--ignore-unrecognized-flags"); for (auto* const profiler_flag : - ProfilingFlags(settings.enable_dart_profiling)) { + ProfilingFlags(settings_.enable_dart_profiling)) { args.push_back(profiler_flag); } @@ -333,7 +316,7 @@ DartVM::DartVM(const Settings& settings, // Enable Dart assertions if we are not running precompiled code. We run non- // precompiled code only in the debug product mode. - bool enable_asserts = !settings.disable_dart_asserts; + bool enable_asserts = !settings_.disable_dart_asserts; #if FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DYNAMIC_PROFILE || \ FLUTTER_RUNTIME_MODE == FLUTTER_RUNTIME_MODE_DYNAMIC_RELEASE @@ -357,24 +340,24 @@ DartVM::DartVM(const Settings& settings, PushBackAll(&args, kDartAssertArgs, arraysize(kDartAssertArgs)); } - if (settings.start_paused) { + if (settings_.start_paused) { PushBackAll(&args, kDartStartPausedArgs, arraysize(kDartStartPausedArgs)); } - if (settings.endless_trace_buffer || settings.trace_startup) { + if (settings_.endless_trace_buffer || settings_.trace_startup) { // If we are tracing startup, make sure the trace buffer is endless so we // don't lose early traces. PushBackAll(&args, kDartEndlessTraceBufferArgs, arraysize(kDartEndlessTraceBufferArgs)); } - if (settings.trace_systrace) { + if (settings_.trace_systrace) { PushBackAll(&args, kDartSystraceTraceBufferArgs, arraysize(kDartSystraceTraceBufferArgs)); PushBackAll(&args, kDartTraceStreamsArgs, arraysize(kDartTraceStreamsArgs)); } - if (settings.trace_startup) { + if (settings_.trace_startup) { PushBackAll(&args, kDartTraceStartupArgs, arraysize(kDartTraceStartupArgs)); } @@ -383,8 +366,8 @@ DartVM::DartVM(const Settings& settings, PushBackAll(&args, kDartTraceStreamsArgs, arraysize(kDartTraceStreamsArgs)); #endif - for (size_t i = 0; i < settings.dart_flags.size(); i++) - args.push_back(settings.dart_flags[i].c_str()); + for (size_t i = 0; i < settings_.dart_flags.size(); i++) + args.push_back(settings_.dart_flags[i].c_str()); char* flags_error = Dart_SetVMFlags(args.size(), args.data()); if (flags_error) { @@ -394,14 +377,14 @@ DartVM::DartVM(const Settings& settings, DartUI::InitForGlobal(); - Dart_SetFileModifiedCallback(&DartFileModifiedCallback); - { TRACE_EVENT0("flutter", "Dart_Initialize"); Dart_InitializeParams params = {}; params.version = DART_INITIALIZE_PARAMS_CURRENT_VERSION; - params.vm_snapshot_data = vm_snapshot_->GetData()->GetSnapshotPointer(); - params.vm_snapshot_instructions = vm_snapshot_->GetInstructionsIfPresent(); + params.vm_snapshot_data = + vm_data_->GetVMSnapshot().GetData()->GetSnapshotPointer(); + params.vm_snapshot_instructions = + vm_data_->GetVMSnapshot().GetInstructionsIfPresent(); params.create = reinterpret_cast( DartIsolate::DartIsolateCreateCallback); params.shutdown = reinterpret_cast( @@ -433,51 +416,50 @@ DartVM::DartVM(const Settings& settings, } } + Dart_SetFileModifiedCallback(&DartFileModifiedCallback); + // Allow streaming of stdout and stderr by the Dart vm. Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback, &ServiceStreamCancelCallback); Dart_SetEmbedderInformationCallback(&EmbedderInformationCallback); + + FML_DLOG(INFO) << "New Dart VM instance created. Instance count: " + << gVMLaunchCount; } DartVM::~DartVM() { if (Dart_CurrentIsolate() != nullptr) { Dart_ExitIsolate(); } + char* result = Dart_Cleanup(); - if (result != nullptr) { - FML_LOG(ERROR) << "Could not cleanly shut down the Dart VM. Message: \"" - << result << "\"."; - free(result); - } + + dart::bin::CleanupDartIo(); + + FML_CHECK(result == nullptr) + << "Could not cleanly shut down the Dart VM. Error: \"" << result + << "\"."; + free(result); + + FML_DLOG(INFO) << "Dart VM instance destroyed. Instance count: " + << gVMLaunchCount; +} + +std::shared_ptr DartVM::GetVMData() const { + return vm_data_; } const Settings& DartVM::GetSettings() const { return settings_; } -const DartSnapshot& DartVM::GetVMSnapshot() const { - return *vm_snapshot_.get(); -} - -IsolateNameServer* DartVM::GetIsolateNameServer() { - return &isolate_name_server_; -} - -fml::RefPtr DartVM::GetIsolateSnapshot() const { - return isolate_snapshot_; -} - -fml::RefPtr DartVM::GetSharedSnapshot() const { - return shared_snapshot_; -} - -ServiceProtocol& DartVM::GetServiceProtocol() { +std::shared_ptr DartVM::GetServiceProtocol() const { return service_protocol_; } -fml::WeakPtr DartVM::GetWeakPtr() { - return weak_factory_.GetWeakPtr(); +std::shared_ptr DartVM::GetIsolateNameServer() const { + return isolate_name_server_; } } // namespace blink diff --git a/engine/src/flutter/runtime/dart_vm.h b/engine/src/flutter/runtime/dart_vm.h index 9a1bde888d..1249490880 100644 --- a/engine/src/flutter/runtime/dart_vm.h +++ b/engine/src/flutter/runtime/dart_vm.h @@ -5,9 +5,8 @@ #ifndef FLUTTER_RUNTIME_DART_VM_H_ #define FLUTTER_RUNTIME_DART_VM_H_ -#include +#include #include -#include #include "flutter/common/settings.h" #include "flutter/fml/build_config.h" @@ -19,59 +18,48 @@ #include "flutter/lib/ui/isolate_name_server/isolate_name_server.h" #include "flutter/runtime/dart_isolate.h" #include "flutter/runtime/dart_snapshot.h" +#include "flutter/runtime/dart_vm_data.h" #include "flutter/runtime/service_protocol.h" #include "third_party/dart/runtime/include/dart_api.h" namespace blink { -class DartVM : public fml::RefCountedThreadSafe { +class DartVM { public: - static fml::RefPtr ForProcess(Settings settings); - - static fml::RefPtr ForProcess( - Settings settings, - fml::RefPtr vm_snapshot, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot); - - static fml::RefPtr ForProcessIfInitialized(); + ~DartVM(); static bool IsRunningPrecompiledCode(); static bool IsKernelMapping(const fml::FileMapping* mapping); + static size_t GetVMLaunchCount(); + const Settings& GetSettings() const; - const DartSnapshot& GetVMSnapshot() const; + std::shared_ptr GetVMData() const; - IsolateNameServer* GetIsolateNameServer(); + std::shared_ptr GetServiceProtocol() const; - fml::RefPtr GetIsolateSnapshot() const; - - fml::RefPtr GetSharedSnapshot() const; - - fml::WeakPtr GetWeakPtr(); - - ServiceProtocol& GetServiceProtocol(); + std::shared_ptr GetIsolateNameServer() const; private: const Settings settings_; - const fml::RefPtr vm_snapshot_; - IsolateNameServer isolate_name_server_; - const fml::RefPtr isolate_snapshot_; - const fml::RefPtr shared_snapshot_; - ServiceProtocol service_protocol_; - fml::WeakPtrFactory weak_factory_; + std::shared_ptr vm_data_; + const std::shared_ptr isolate_name_server_; + const std::shared_ptr service_protocol_; - DartVM(const Settings& settings, - fml::RefPtr vm_snapshot, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot); + friend class DartVMRef; - ~DartVM(); + static std::shared_ptr Create( + Settings settings, + fml::RefPtr vm_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, + std::shared_ptr isolate_name_server); + + DartVM(std::shared_ptr data, + std::shared_ptr isolate_name_server); - FML_FRIEND_REF_COUNTED_THREAD_SAFE(DartVM); - FML_FRIEND_MAKE_REF_COUNTED(DartVM); FML_DISALLOW_COPY_AND_ASSIGN(DartVM); }; diff --git a/engine/src/flutter/runtime/dart_vm_data.cc b/engine/src/flutter/runtime/dart_vm_data.cc new file mode 100644 index 0000000000..a29074f79c --- /dev/null +++ b/engine/src/flutter/runtime/dart_vm_data.cc @@ -0,0 +1,79 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/runtime/dart_vm_data.h" + +namespace blink { + +std::shared_ptr DartVMData::Create( + Settings settings, + fml::RefPtr vm_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot) { + if (!vm_snapshot || !vm_snapshot->IsValid()) { + // Caller did not provide a valid VM snapshot. Attempt to infer one + // from the settings. + vm_snapshot = DartSnapshot::VMSnapshotFromSettings(settings); + if (!vm_snapshot) { + FML_LOG(ERROR) + << "VM snapshot invalid and could not be inferred from settings."; + return {}; + } + } + + if (!isolate_snapshot || !isolate_snapshot->IsValid()) { + // Caller did not provide a valid isolate snapshot. Attempt to infer one + // from the settings. + isolate_snapshot = DartSnapshot::IsolateSnapshotFromSettings(settings); + if (!isolate_snapshot) { + FML_LOG(ERROR) << "Isolate snapshot invalid and could not be inferred " + "from settings."; + return {}; + } + } + + if (!shared_snapshot || !shared_snapshot->IsValid()) { + shared_snapshot = DartSnapshot::Empty(); + if (!shared_snapshot) { + FML_LOG(ERROR) << "Shared snapshot invalid."; + return {}; + } + } + + return std::shared_ptr(new DartVMData( + std::move(settings), // + std::move(vm_snapshot), // + std::move(isolate_snapshot), // + std::move(shared_snapshot) // + )); +} + +DartVMData::DartVMData(Settings settings, + fml::RefPtr vm_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot) + : settings_(settings), + vm_snapshot_(vm_snapshot), + isolate_snapshot_(isolate_snapshot), + shared_snapshot_(shared_snapshot) {} + +DartVMData::~DartVMData() = default; + +const Settings& DartVMData::GetSettings() const { + return settings_; +} + +const DartSnapshot& DartVMData::GetVMSnapshot() const { + return *vm_snapshot_; +} + +fml::RefPtr DartVMData::GetIsolateSnapshot() const { + return isolate_snapshot_; +} + +fml::RefPtr DartVMData::GetSharedSnapshot() const { + return shared_snapshot_; +} + +} // namespace blink diff --git a/engine/src/flutter/runtime/dart_vm_data.h b/engine/src/flutter/runtime/dart_vm_data.h new file mode 100644 index 0000000000..906cad1a4c --- /dev/null +++ b/engine/src/flutter/runtime/dart_vm_data.h @@ -0,0 +1,47 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_RUNTIME_DART_VM_DATA_H_ +#define FLUTTER_RUNTIME_DART_VM_DATA_H_ + +#include "flutter/fml/macros.h" +#include "flutter/runtime/dart_snapshot.h" + +namespace blink { + +class DartVMData { + public: + static std::shared_ptr Create( + Settings settings, + fml::RefPtr vm_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot); + + ~DartVMData(); + + const Settings& GetSettings() const; + + const DartSnapshot& GetVMSnapshot() const; + + fml::RefPtr GetIsolateSnapshot() const; + + fml::RefPtr GetSharedSnapshot() const; + + private: + const Settings settings_; + const fml::RefPtr vm_snapshot_; + const fml::RefPtr isolate_snapshot_; + const fml::RefPtr shared_snapshot_; + + DartVMData(Settings settings, + fml::RefPtr vm_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot); + + FML_DISALLOW_COPY_AND_ASSIGN(DartVMData); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_VM_DATA_H_ diff --git a/engine/src/flutter/runtime/dart_vm_lifecycle.cc b/engine/src/flutter/runtime/dart_vm_lifecycle.cc new file mode 100644 index 0000000000..e34132dd6b --- /dev/null +++ b/engine/src/flutter/runtime/dart_vm_lifecycle.cc @@ -0,0 +1,112 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/runtime/dart_vm_lifecycle.h" + +#include + +namespace blink { + +// We need to explicitly put the constructor and destructor of the DartVM in the +// critical section. All accesses (not just const members) to the global VM +// object weak pointer are behind this mutex. +static std::mutex gVMMutex; +static std::weak_ptr gVM FML_GUARDED_BY(gVMMutex); + +// We are going to be modifying more than just the control blocks of the +// following weak pointers (in the |Create| case where an old VM could not be +// reused). Ideally, we would use |std::atomic>| specialization +// but that is only available since C++20. We don't expect contention on these +// locks so we just use one mutex for all. +static std::mutex gVMDependentsMutex; +static std::weak_ptr gVMData + FML_GUARDED_BY(gVMDependentsMutex); +static std::weak_ptr gVMServiceProtocol + FML_GUARDED_BY(gVMDependentsMutex); +static std::weak_ptr gVMIsolateNameServer + FML_GUARDED_BY(gVMDependentsMutex); + +DartVMRef::DartVMRef(std::shared_ptr vm) : vm_(vm) {} + +DartVMRef::DartVMRef(DartVMRef&& other) = default; + +DartVMRef::~DartVMRef() { + if (!vm_) { + // If there is no valid VM (possible via a move), there is no way that the + // decrement on the shared pointer can cause a collection. Avoid acquiring + // the lifecycle lock in this case. This is just working around a + // pessimization and not required for correctness. + return; + } + std::lock_guard lifecycle_lock(gVMMutex); + vm_.reset(); +} + +DartVMRef DartVMRef::Create(Settings settings, + fml::RefPtr vm_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot) { + std::lock_guard lifecycle_lock(gVMMutex); + + // If there is already a running VM in the process, grab a strong reference to + // it. + if (auto vm = gVM.lock()) { + FML_DLOG(WARNING) << "Attempted to create a VM in a process where one was " + "already running. Ignoring arguments for current VM " + "create call and reusing the old VM."; + // There was already a running VM in the process, + return DartVMRef{std::move(vm)}; + } + + std::lock_guard dependents_lock(gVMDependentsMutex); + + gVMData.reset(); + gVMServiceProtocol.reset(); + gVMIsolateNameServer.reset(); + gVM.reset(); + + // If there is no VM in the process. Initialize one, hold the weak reference + // and pass a strong reference to the caller. + auto isolate_name_server = std::make_shared(); + auto vm = DartVM::Create(std::move(settings), // + std::move(vm_snapshot), // + std::move(isolate_snapshot), // + std::move(shared_snapshot), // + isolate_name_server // + ); + + if (!vm) { + FML_LOG(ERROR) << "Could not create Dart VM instance."; + return {nullptr}; + } + + gVMData = vm->GetVMData(); + gVMServiceProtocol = vm->GetServiceProtocol(); + gVMIsolateNameServer = isolate_name_server; + + gVM = vm; + return DartVMRef{std::move(vm)}; +} + +bool DartVMRef::IsInstanceRunning() { + std::lock_guard lock(gVMMutex); + return !gVM.expired(); +} + +std::shared_ptr DartVMRef::GetVMData() { + std::lock_guard lock(gVMDependentsMutex); + return gVMData.lock(); +} + +std::shared_ptr DartVMRef::GetServiceProtocol() { + std::lock_guard lock(gVMDependentsMutex); + return gVMServiceProtocol.lock(); +} + +std::shared_ptr DartVMRef::GetIsolateNameServer() { + std::lock_guard lock(gVMDependentsMutex); + return gVMIsolateNameServer.lock(); +} + +} // namespace blink diff --git a/engine/src/flutter/runtime/dart_vm_lifecycle.h b/engine/src/flutter/runtime/dart_vm_lifecycle.h new file mode 100644 index 0000000000..cfe1f1b646 --- /dev/null +++ b/engine/src/flutter/runtime/dart_vm_lifecycle.h @@ -0,0 +1,72 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_RUNTIME_DART_VM_LIFECYCLE_H_ +#define FLUTTER_RUNTIME_DART_VM_LIFECYCLE_H_ + +#include + +#include "flutter/fml/macros.h" +#include "flutter/lib/ui/isolate_name_server/isolate_name_server.h" +#include "flutter/runtime/dart_vm.h" +#include "flutter/runtime/service_protocol.h" + +namespace blink { + +// A strong reference to the Dart VM. There can only be one VM running in the +// process at any given time. A reference to the VM may only be obtained via the +// |Create| method. In case there is already a running instance of the VM in the +// process, a strong reference to that VM is obtained and the arguments to the +// |Create| call ignored. If there is no VM already running in the process, a VM +// is initialized in a thread safe manner and returned to the caller. The VM +// will shutdown only when all callers relinquish their references (by +// collecting their instances of this class). +// +// DartVMRef instances may be created on any thread. +class DartVMRef { + public: + FML_WARN_UNUSED_RESULT + static DartVMRef Create(Settings settings, + fml::RefPtr vm_snapshot = nullptr, + fml::RefPtr isolate_snapshot = nullptr, + fml::RefPtr shared_snapshot = nullptr); + + DartVMRef(DartVMRef&&); + + ~DartVMRef(); + + // This is an inherently racy way to check if a VM instance is running and + // should not be used outside of unit-tests where there is a known threading + // model. + static bool IsInstanceRunning(); + + static std::shared_ptr GetVMData(); + + static std::shared_ptr GetServiceProtocol(); + + static std::shared_ptr GetIsolateNameServer(); + + operator bool() const { return static_cast(vm_); } + + DartVM* operator->() { + FML_DCHECK(vm_); + return vm_.get(); + } + + DartVM* operator&() { + FML_DCHECK(vm_); + return vm_.get(); + } + + private: + std::shared_ptr vm_; + + DartVMRef(std::shared_ptr vm); + + FML_DISALLOW_COPY_AND_ASSIGN(DartVMRef); +}; + +} // namespace blink + +#endif // FLUTTER_RUNTIME_DART_VM_LIFECYCLE_H_ diff --git a/engine/src/flutter/runtime/dart_vm_unittests.cc b/engine/src/flutter/runtime/dart_vm_unittests.cc index 21e3d3d3fa..b3653333b5 100644 --- a/engine/src/flutter/runtime/dart_vm_unittests.cc +++ b/engine/src/flutter/runtime/dart_vm_unittests.cc @@ -3,25 +3,36 @@ // found in the LICENSE file. #include "flutter/runtime/dart_vm.h" +#include "flutter/runtime/dart_vm_lifecycle.h" +#include "flutter/shell/common/thread_host.h" +#include "flutter/testing/testing.h" +#include "flutter/testing/thread_test.h" #include "gtest/gtest.h" namespace blink { -TEST(DartVM, SimpleInitialization) { - Settings settings = {}; +static Settings GetTestSettings() { + Settings settings; + settings.verbose_logging = true; settings.task_observer_add = [](intptr_t, fml::closure) {}; settings.task_observer_remove = [](intptr_t) {}; - auto vm = DartVM::ForProcess(settings); - ASSERT_TRUE(vm); - ASSERT_EQ(vm, DartVM::ForProcess(settings)); + return settings; +} + +TEST(DartVM, SimpleInitialization) { + auto vm1 = DartVMRef::Create(GetTestSettings()); + ASSERT_TRUE(vm1); + + // Multiple initializations should return the same VM. + auto vm2 = DartVMRef::Create(GetTestSettings()); + ASSERT_TRUE(vm2); + + ASSERT_EQ(&vm1, &vm2); ASSERT_FALSE(DartVM::IsRunningPrecompiledCode()); } TEST(DartVM, SimpleIsolateNameServer) { - Settings settings = {}; - settings.task_observer_add = [](intptr_t, fml::closure) {}; - settings.task_observer_remove = [](intptr_t) {}; - auto vm = DartVM::ForProcess(settings); + auto vm = DartVMRef::Create(GetTestSettings()); auto ns = vm->GetIsolateNameServer(); ASSERT_EQ(ns->LookupIsolatePortByName("foobar"), ILLEGAL_PORT); ASSERT_FALSE(ns->RemoveIsolateNameMapping("foobar")); @@ -31,4 +42,79 @@ TEST(DartVM, SimpleIsolateNameServer) { ASSERT_TRUE(ns->RemoveIsolateNameMapping("foobar")); } +TEST(DartVM, CanReinitializeVMOverAndOver) { + size_t vm_launch_count = DartVM::GetVMLaunchCount(); + for (size_t i = 0; i < 1000; ++i) { + FML_LOG(INFO) << "Run " << i + 1; + + // VM should not already be running. + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + + auto vm = DartVMRef::Create(GetTestSettings()); + ASSERT_TRUE(vm); + size_t new_vm_launch_count = DartVM::GetVMLaunchCount(); + ASSERT_EQ(vm_launch_count + 1, new_vm_launch_count); + vm_launch_count = new_vm_launch_count; + + // VM should now be running + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + } +} + +using DartVMThreadTest = ::testing::ThreadTest; + +TEST_F(DartVMThreadTest, CanRunIsolatesInANewVM) { + for (size_t i = 0; i < 1000; ++i) { + FML_LOG(INFO) << "Run " << i + 1; + size_t vm_launch_count = DartVM::GetVMLaunchCount(); + + // VM should not already be running. + ASSERT_FALSE(DartVMRef::IsInstanceRunning()); + + auto vm = DartVMRef::Create(GetTestSettings()); + ASSERT_TRUE(vm); + + // VM should not already be running. + ASSERT_TRUE(DartVMRef::IsInstanceRunning()); + + size_t new_vm_launch_count = DartVM::GetVMLaunchCount(); + ASSERT_EQ(vm_launch_count + 1, new_vm_launch_count); + + Settings settings = {}; + + settings.task_observer_add = [](intptr_t, fml::closure) {}; + settings.task_observer_remove = [](intptr_t) {}; + + auto labels = testing::GetCurrentTestName() + std::to_string(i); + shell::ThreadHost host(labels, shell::ThreadHost::Type::UI | + shell::ThreadHost::Type::GPU | + shell::ThreadHost::Type::IO); + + TaskRunners task_runners( + labels, // task runner labels + GetCurrentTaskRunner(), // platform task runner + host.gpu_thread->GetTaskRunner(), // GPU task runner + host.ui_thread->GetTaskRunner(), // UI task runner + host.io_thread->GetTaskRunner() // IO task runner + ); + + auto weak_isolate = DartIsolate::CreateRootIsolate( + vm->GetVMData()->GetSettings(), // settings + vm->GetVMData()->GetIsolateSnapshot(), // isolate snapshot + vm->GetVMData()->GetSharedSnapshot(), // shared snapshot + std::move(task_runners), // task runners + nullptr, // window + {}, // snapshot delegate + {}, // io manager + "main.dart", // advisory uri + "main" // advisory entrypoint + ); + + auto root_isolate = weak_isolate.lock(); + ASSERT_TRUE(root_isolate); + ASSERT_EQ(root_isolate->GetPhase(), DartIsolate::Phase::LibrariesSetup); + ASSERT_TRUE(root_isolate->Shutdown()); + } +} + } // namespace blink diff --git a/engine/src/flutter/runtime/runtime_controller.cc b/engine/src/flutter/runtime/runtime_controller.cc index b57f2232ac..d3850398d5 100644 --- a/engine/src/flutter/runtime/runtime_controller.cc +++ b/engine/src/flutter/runtime/runtime_controller.cc @@ -17,8 +17,8 @@ namespace blink { RuntimeController::RuntimeController( RuntimeDelegate& p_client, DartVM* p_vm, - fml::RefPtr p_isolate_snapshot, - fml::RefPtr p_shared_snapshot, + fml::RefPtr p_isolate_snapshot, + fml::RefPtr p_shared_snapshot, TaskRunners p_task_runners, fml::WeakPtr p_snapshot_delegate, fml::WeakPtr p_io_manager, @@ -40,8 +40,8 @@ RuntimeController::RuntimeController( RuntimeController::RuntimeController( RuntimeDelegate& p_client, DartVM* p_vm, - fml::RefPtr p_isolate_snapshot, - fml::RefPtr p_shared_snapshot, + fml::RefPtr p_isolate_snapshot, + fml::RefPtr p_shared_snapshot, TaskRunners p_task_runners, fml::WeakPtr p_snapshot_delegate, fml::WeakPtr p_io_manager, @@ -61,7 +61,7 @@ RuntimeController::RuntimeController( idle_notification_callback_(idle_notification_callback), window_data_(std::move(p_window_data)), root_isolate_( - DartIsolate::CreateRootIsolate(vm_, + DartIsolate::CreateRootIsolate(vm_->GetVMData()->GetSettings(), isolate_snapshot_, shared_snapshot_, task_runners_, diff --git a/engine/src/flutter/runtime/runtime_controller.h b/engine/src/flutter/runtime/runtime_controller.h index c913795b8e..6d9baf7573 100644 --- a/engine/src/flutter/runtime/runtime_controller.h +++ b/engine/src/flutter/runtime/runtime_controller.h @@ -30,8 +30,8 @@ class RuntimeController final : public WindowClient { public: RuntimeController(RuntimeDelegate& client, DartVM* vm, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, TaskRunners task_runners, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, @@ -118,8 +118,8 @@ class RuntimeController final : public WindowClient { RuntimeDelegate& client_; DartVM* const vm_; - fml::RefPtr isolate_snapshot_; - fml::RefPtr shared_snapshot_; + fml::RefPtr isolate_snapshot_; + fml::RefPtr shared_snapshot_; TaskRunners task_runners_; fml::WeakPtr snapshot_delegate_; fml::WeakPtr io_manager_; @@ -132,8 +132,8 @@ class RuntimeController final : public WindowClient { RuntimeController(RuntimeDelegate& client, DartVM* vm, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, TaskRunners task_runners, fml::WeakPtr snapshot_delegate, fml::WeakPtr io_manager, diff --git a/engine/src/flutter/shell/common/engine.cc b/engine/src/flutter/shell/common/engine.cc index 084216072e..d6a790be40 100644 --- a/engine/src/flutter/shell/common/engine.cc +++ b/engine/src/flutter/shell/common/engine.cc @@ -37,8 +37,8 @@ static constexpr char kSettingsChannel[] = "flutter/settings"; Engine::Engine(Delegate& delegate, blink::DartVM& vm, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, blink::TaskRunners task_runners, blink::Settings settings, std::unique_ptr animator, diff --git a/engine/src/flutter/shell/common/engine.h b/engine/src/flutter/shell/common/engine.h index b390b36a2b..22160cb7ee 100644 --- a/engine/src/flutter/shell/common/engine.h +++ b/engine/src/flutter/shell/common/engine.h @@ -56,8 +56,8 @@ class Engine final : public blink::RuntimeDelegate { Engine(Delegate& delegate, blink::DartVM& vm, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, blink::TaskRunners task_runners, blink::Settings settings, std::unique_ptr animator, diff --git a/engine/src/flutter/shell/common/shell.cc b/engine/src/flutter/shell/common/shell.cc index c1dec19f6a..2949e6dec5 100644 --- a/engine/src/flutter/shell/common/shell.cc +++ b/engine/src/flutter/shell/common/shell.cc @@ -36,17 +36,20 @@ namespace shell { constexpr char kSkiaChannel[] = "flutter/skia"; std::unique_ptr Shell::CreateShellOnPlatformThread( + blink::DartVMRef vm, blink::TaskRunners task_runners, blink::Settings settings, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer) { if (!task_runners.IsValid()) { + FML_LOG(ERROR) << "Task runners to run the shell were invalid."; return nullptr; } - auto shell = std::unique_ptr(new Shell(task_runners, settings)); + auto shell = + std::unique_ptr(new Shell(std::move(vm), task_runners, settings)); // Create the platform view on the platform thread (this thread). auto platform_view = on_create_platform_view(*shell.get()); @@ -75,6 +78,7 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( &platform_view, // io_task_runner // ]() { + TRACE_EVENT0("flutter", "ShellSetupIOSubsystem"); io_manager = std::make_unique( platform_view->CreateResourceContext(), io_task_runner); io_latch.Signal(); @@ -92,6 +96,7 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( shell = shell.get(), // &snapshot_delegate // ]() { + TRACE_EVENT0("flutter", "ShellSetupGPUSubsystem"); if (auto new_rasterizer = on_create_rasterizer(*shell)) { rasterizer = std::move(new_rasterizer); snapshot_delegate = rasterizer->GetSnapshotDelegate(); @@ -115,6 +120,7 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( snapshot_delegate = std::move(snapshot_delegate), // io_manager = io_manager->GetWeakPtr() // ]() mutable { + TRACE_EVENT0("flutter", "ShellSetupUISubsystem"); const auto& task_runners = shell->GetTaskRunners(); // The animator is owned by the UI thread but it gets its vsync pulses @@ -123,7 +129,7 @@ std::unique_ptr Shell::CreateShellOnPlatformThread( std::move(vsync_waiter)); engine = std::make_unique(*shell, // - shell->GetDartVM(), // + *shell->GetDartVM(), // std::move(isolate_snapshot), // std::move(shared_snapshot), // task_runners, // @@ -204,26 +210,35 @@ std::unique_ptr Shell::Create( Shell::CreateCallback on_create_rasterizer) { PerformInitializationTasks(settings); - auto vm = blink::DartVM::ForProcess(settings); + TRACE_EVENT0("flutter", "Shell::Create"); + + auto vm = blink::DartVMRef::Create(settings); FML_CHECK(vm) << "Must be able to initialize the VM."; + + auto vm_data = vm->GetVMData(); + return Shell::Create(std::move(task_runners), // std::move(settings), // - vm->GetIsolateSnapshot(), // - blink::DartSnapshot::Empty(), // + vm_data->GetIsolateSnapshot(), // isolate snapshot + blink::DartSnapshot::Empty(), // shared snapshot std::move(on_create_platform_view), // - std::move(on_create_rasterizer) // + std::move(on_create_rasterizer), // + std::move(vm) // ); } std::unique_ptr Shell::Create( blink::TaskRunners task_runners, blink::Settings settings, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, Shell::CreateCallback on_create_platform_view, - Shell::CreateCallback on_create_rasterizer) { + Shell::CreateCallback on_create_rasterizer, + blink::DartVMRef vm) { PerformInitializationTasks(settings); + TRACE_EVENT0("flutter", "Shell::CreateWithSnapshots"); + if (!task_runners.IsValid() || !on_create_platform_view || !on_create_rasterizer) { return nullptr; @@ -233,16 +248,18 @@ std::unique_ptr Shell::Create( std::unique_ptr shell; fml::TaskRunner::RunNowOrPostTask( task_runners.GetPlatformTaskRunner(), - [&latch, // - &shell, // - task_runners = std::move(task_runners), // - settings, // - isolate_snapshot = std::move(isolate_snapshot), // - shared_snapshot = std::move(shared_snapshot), // - on_create_platform_view, // - on_create_rasterizer // - ]() { - shell = CreateShellOnPlatformThread(std::move(task_runners), // + fml::MakeCopyable([&latch, // + vm = std::move(vm), // + &shell, // + task_runners = std::move(task_runners), // + settings, // + isolate_snapshot = std::move(isolate_snapshot), // + shared_snapshot = std::move(shared_snapshot), // + on_create_platform_view, // + on_create_rasterizer // + ]() mutable { + shell = CreateShellOnPlatformThread(std::move(vm), + std::move(task_runners), // settings, // std::move(isolate_snapshot), // std::move(shared_snapshot), // @@ -250,15 +267,18 @@ std::unique_ptr Shell::Create( on_create_rasterizer // ); latch.Signal(); - }); + })); latch.Wait(); return shell; } -Shell::Shell(blink::TaskRunners task_runners, blink::Settings settings) +Shell::Shell(blink::DartVMRef vm, + blink::TaskRunners task_runners, + blink::Settings settings) : task_runners_(std::move(task_runners)), settings_(std::move(settings)), - vm_(blink::DartVM::ForProcess(settings_)) { + vm_(std::move(vm)) { + FML_CHECK(vm_) << "Must have access to VM to create a shell."; FML_DCHECK(task_runners_.IsValid()); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); @@ -300,9 +320,7 @@ Shell::~Shell() { PersistentCache::GetCacheForProcess()->RemoveWorkerTaskRunner( task_runners_.GetIOTaskRunner()); - if (auto vm = blink::DartVM::ForProcessIfInitialized()) { - vm->GetServiceProtocol().RemoveHandler(this); - } + vm_->GetServiceProtocol()->RemoveHandler(this); fml::AutoResetWaitableEvent ui_latch, gpu_latch, platform_latch, io_latch; @@ -373,9 +391,7 @@ bool Shell::Setup(std::unique_ptr platform_view, is_setup_ = true; - if (auto vm = blink::DartVM::ForProcessIfInitialized()) { - vm->GetServiceProtocol().AddHandler(this, GetServiceProtocolDescription()); - } + vm_->GetServiceProtocol()->AddHandler(this, GetServiceProtocolDescription()); PersistentCache::GetCacheForProcess()->AddWorkerTaskRunner( task_runners_.GetIOTaskRunner()); @@ -406,12 +422,13 @@ fml::WeakPtr Shell::GetPlatformView() { return platform_view_->GetWeakPtr(); } -blink::DartVM& Shell::GetDartVM() const { - return *vm_; +blink::DartVM* Shell::GetDartVM() { + return &vm_; } // |shell::PlatformView::Delegate| void Shell::OnPlatformViewCreated(std::unique_ptr surface) { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewCreated"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); @@ -469,6 +486,7 @@ void Shell::OnPlatformViewCreated(std::unique_ptr surface) { // |shell::PlatformView::Delegate| void Shell::OnPlatformViewDestroyed() { + TRACE_EVENT0("flutter", "Shell::OnPlatformViewDestroyed"); FML_DCHECK(is_setup_); FML_DCHECK(task_runners_.GetPlatformTaskRunner()->RunsTasksOnCurrentThread()); @@ -804,10 +822,8 @@ void Shell::OnPreEngineRestart() { // |shell::Engine::Delegate| void Shell::UpdateIsolateDescription(const std::string isolate_name, int64_t isolate_port) { - if (auto vm = blink::DartVM::ForProcessIfInitialized()) { - Handler::Description description(isolate_port, isolate_name); - vm->GetServiceProtocol().SetHandlerDescription(this, description); - } + Handler::Description description(isolate_port, isolate_name); + vm_->GetServiceProtocol()->SetHandlerDescription(this, description); } // |blink::ServiceProtocol::Handler| diff --git a/engine/src/flutter/shell/common/shell.h b/engine/src/flutter/shell/common/shell.h index 17b0f17056..864235473c 100644 --- a/engine/src/flutter/shell/common/shell.h +++ b/engine/src/flutter/shell/common/shell.h @@ -23,6 +23,7 @@ #include "flutter/lib/ui/semantics/custom_accessibility_action.h" #include "flutter/lib/ui/semantics/semantics_node.h" #include "flutter/lib/ui/window/platform_message.h" +#include "flutter/runtime/dart_vm_lifecycle.h" #include "flutter/runtime/service_protocol.h" #include "flutter/shell/common/animator.h" #include "flutter/shell/common/engine.h" @@ -54,10 +55,11 @@ class Shell final : public PlatformView::Delegate, static std::unique_ptr Create( blink::TaskRunners task_runners, blink::Settings settings, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, CreateCallback on_create_platform_view, - CreateCallback on_create_rasterizer); + CreateCallback on_create_rasterizer, + blink::DartVMRef vm); ~Shell(); @@ -71,7 +73,7 @@ class Shell final : public PlatformView::Delegate, fml::WeakPtr GetPlatformView(); - blink::DartVM& GetDartVM() const; + blink::DartVM* GetDartVM(); bool IsSetup() const; @@ -85,7 +87,7 @@ class Shell final : public PlatformView::Delegate, const blink::TaskRunners task_runners_; const blink::Settings settings_; - fml::RefPtr vm_; + blink::DartVMRef vm_; std::unique_ptr platform_view_; // on platform task runner std::unique_ptr engine_; // on UI task runner std::unique_ptr rasterizer_; // on GPU task runner @@ -98,16 +100,19 @@ class Shell final : public PlatformView::Delegate, > service_protocol_handlers_; bool is_setup_ = false; - uint64_t next_pointer_flow_id_ = 0; Shell(blink::TaskRunners task_runners, blink::Settings settings); + Shell(blink::DartVMRef vm, + blink::TaskRunners task_runners, + blink::Settings settings); static std::unique_ptr CreateShellOnPlatformThread( + blink::DartVMRef vm, blink::TaskRunners task_runners, blink::Settings settings, - fml::RefPtr isolate_snapshot, - fml::RefPtr shared_snapshot, + fml::RefPtr isolate_snapshot, + fml::RefPtr shared_snapshot, Shell::CreateCallback on_create_platform_view, Shell::CreateCallback on_create_rasterizer); diff --git a/engine/src/flutter/testing/testing.cc b/engine/src/flutter/testing/testing.cc index 93e135e7ab..c738219541 100644 --- a/engine/src/flutter/testing/testing.cc +++ b/engine/src/flutter/testing/testing.cc @@ -6,6 +6,8 @@ namespace testing { -// +std::string GetCurrentTestName() { + return UnitTest::GetInstance()->current_test_info()->name(); +} } // namespace testing diff --git a/engine/src/flutter/testing/testing.h b/engine/src/flutter/testing/testing.h index dac2d229b9..0662055fea 100644 --- a/engine/src/flutter/testing/testing.h +++ b/engine/src/flutter/testing/testing.h @@ -5,6 +5,8 @@ #ifndef TESTING_TESTING_H_ #define TESTING_TESTING_H_ +#include + #include "gtest/gtest.h" namespace testing { @@ -14,6 +16,8 @@ namespace testing { // error. const char* GetFixturesPath(); +std::string GetCurrentTestName(); + } // namespace testing #endif // TESTING_TESTING_H_