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_