| // 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.h" |
| #include "flutter/runtime/dart_vm_lifecycle.h" |
| #include "flutter/runtime/platform_isolate_manager.h" |
| #include "flutter/testing/fixture_test.h" |
| #include "flutter/testing/testing.h" |
| |
| namespace flutter { |
| namespace testing { |
| |
| struct IsolateData { |
| PlatformIsolateManager* mgr; |
| Dart_Isolate isolate = nullptr; |
| bool is_shutdown = false; |
| bool was_registered = false; |
| explicit IsolateData(PlatformIsolateManager* _mgr) : mgr(_mgr) {} |
| }; |
| |
| // The IsolateDataMap is a map from Dart_Isolate to a *vector* of IsolateData, |
| // because Dart_Isolates are frequently reused after shutdown, and we want the |
| // IsolateData objects to live as long as the map itself. The last element of |
| // the vector is always the currently active IsolateData, and the other elements |
| // refer to isolates that have been shutdown. |
| using IsolateDataMap = |
| std::unordered_map<Dart_Isolate, std::vector<std::unique_ptr<IsolateData>>>; |
| |
| // Using a thread local isolate data map so that MultithreadedCreation test |
| // can avoid using locks while creating isolates on multiple threads. A lock |
| // would sync up the threads, so would defeat the purpose of the test. |
| static thread_local std::unique_ptr<IsolateDataMap> isolate_data_map_; |
| |
| class PlatformIsolateManagerTest : public FixtureTest { |
| public: |
| PlatformIsolateManagerTest() {} |
| |
| void TestWithRootIsolate(const std::function<void()>& test) { |
| ASSERT_FALSE(DartVMRef::IsInstanceRunning()); |
| auto settings = CreateSettingsForFixture(); |
| auto vm_ref = DartVMRef::Create(settings); |
| ASSERT_TRUE(vm_ref); |
| auto vm_data = vm_ref.GetVMData(); |
| ASSERT_TRUE(vm_data); |
| |
| TaskRunners task_runners(GetCurrentTestName(), // |
| GetCurrentTaskRunner(), // |
| GetCurrentTaskRunner(), // |
| GetCurrentTaskRunner(), // |
| GetCurrentTaskRunner() // |
| ); |
| |
| auto isolate_configuration = |
| IsolateConfiguration::InferFromSettings(settings); |
| |
| UIDartState::Context context(task_runners); |
| context.advisory_script_uri = "main.dart"; |
| context.advisory_script_entrypoint = "main"; |
| auto weak_isolate = DartIsolate::CreateRunningRootIsolate( |
| vm_data->GetSettings(), // settings |
| vm_data->GetIsolateSnapshot(), // isolate snapshot |
| nullptr, // platform configuration |
| DartIsolate::Flags{}, // flags |
| nullptr, // root_isolate_create_callback |
| settings.isolate_create_callback, // isolate create callback |
| settings.isolate_shutdown_callback, // isolate shutdown callback |
| "main", // dart entrypoint |
| std::nullopt, // dart entrypoint library |
| {}, // dart entrypoint arguments |
| std::move(isolate_configuration), // isolate configuration |
| context // engine context |
| ); |
| root_isolate_ = weak_isolate.lock()->isolate(); |
| ASSERT_TRUE(root_isolate_); |
| |
| test(); |
| |
| Dart_EnterIsolate(root_isolate_); |
| Dart_ShutdownIsolate(); |
| } |
| |
| Dart_Isolate CreateAndRegisterIsolate(PlatformIsolateManager* mgr) { |
| if (isolate_data_map_.get() == nullptr) { |
| isolate_data_map_.reset(new IsolateDataMap()); |
| } |
| |
| IsolateData* isolate_data = new IsolateData(mgr); |
| char* error = nullptr; |
| Dart_Isolate isolate = |
| Dart_CreateIsolateInGroup(root_isolate_, "TestIsolate", OnShutdown, |
| nullptr, isolate_data, &error); |
| isolate_data->isolate = isolate; |
| EXPECT_TRUE(isolate); |
| Dart_ExitIsolate(); |
| |
| (*isolate_data_map_.get())[isolate].push_back( |
| std::unique_ptr<IsolateData>(isolate_data)); |
| isolate_data->was_registered = mgr->RegisterPlatformIsolate(isolate); |
| |
| return isolate; |
| } |
| |
| bool IsolateIsShutdown(Dart_Isolate isolate) { |
| EXPECT_EQ(1u, isolate_data_map_.get()->count(isolate)); |
| EXPECT_LT(0u, (*isolate_data_map_.get())[isolate].size()); |
| return (*isolate_data_map_.get())[isolate].back()->is_shutdown; |
| } |
| |
| bool IsolateWasRegistered(Dart_Isolate isolate) { |
| EXPECT_EQ(1u, isolate_data_map_.get()->count(isolate)); |
| EXPECT_LT(0u, (*isolate_data_map_.get())[isolate].size()); |
| return (*isolate_data_map_.get())[isolate].back()->was_registered; |
| } |
| |
| private: |
| Dart_Isolate root_isolate_ = nullptr; |
| |
| static void OnShutdown(void*, void* raw_isolate_data) { |
| IsolateData* isolate_data = |
| reinterpret_cast<IsolateData*>(raw_isolate_data); |
| EXPECT_TRUE(isolate_data->isolate); |
| EXPECT_FALSE(isolate_data->is_shutdown); |
| isolate_data->is_shutdown = true; |
| if (isolate_data->was_registered) { |
| isolate_data->mgr->RemovePlatformIsolate(isolate_data->isolate); |
| } |
| } |
| |
| FML_DISALLOW_COPY_AND_ASSIGN(PlatformIsolateManagerTest); |
| }; |
| |
| TEST_F(PlatformIsolateManagerTest, OrdinaryFlow) { |
| TestWithRootIsolate([this]() { |
| PlatformIsolateManager mgr; |
| EXPECT_FALSE(mgr.HasShutdown()); |
| EXPECT_FALSE(mgr.HasShutdownMaybeFalseNegative()); |
| |
| Dart_Isolate isolateA = CreateAndRegisterIsolate(&mgr); |
| ASSERT_TRUE(isolateA); |
| EXPECT_FALSE(IsolateIsShutdown(isolateA)); |
| EXPECT_TRUE(IsolateWasRegistered(isolateA)); |
| EXPECT_TRUE(mgr.IsRegisteredForTestingOnly(isolateA)); |
| |
| Dart_Isolate isolateB = CreateAndRegisterIsolate(&mgr); |
| ASSERT_TRUE(isolateB); |
| EXPECT_FALSE(IsolateIsShutdown(isolateB)); |
| EXPECT_TRUE(IsolateWasRegistered(isolateB)); |
| EXPECT_TRUE(mgr.IsRegisteredForTestingOnly(isolateB)); |
| |
| mgr.ShutdownPlatformIsolates(); |
| EXPECT_TRUE(mgr.HasShutdown()); |
| EXPECT_TRUE(mgr.HasShutdownMaybeFalseNegative()); |
| |
| EXPECT_TRUE(IsolateIsShutdown(isolateA)); |
| EXPECT_FALSE(mgr.IsRegisteredForTestingOnly(isolateA)); |
| EXPECT_TRUE(IsolateIsShutdown(isolateB)); |
| EXPECT_FALSE(mgr.IsRegisteredForTestingOnly(isolateB)); |
| }); |
| } |
| |
| TEST_F(PlatformIsolateManagerTest, EarlyShutdown) { |
| TestWithRootIsolate([this]() { |
| PlatformIsolateManager mgr; |
| EXPECT_FALSE(mgr.HasShutdown()); |
| |
| Dart_Isolate isolateA = CreateAndRegisterIsolate(&mgr); |
| ASSERT_TRUE(isolateA); |
| EXPECT_FALSE(IsolateIsShutdown(isolateA)); |
| EXPECT_TRUE(IsolateWasRegistered(isolateA)); |
| EXPECT_TRUE(mgr.IsRegisteredForTestingOnly(isolateA)); |
| |
| Dart_Isolate isolateB = CreateAndRegisterIsolate(&mgr); |
| ASSERT_TRUE(isolateB); |
| EXPECT_FALSE(IsolateIsShutdown(isolateB)); |
| EXPECT_TRUE(IsolateWasRegistered(isolateB)); |
| EXPECT_TRUE(mgr.IsRegisteredForTestingOnly(isolateB)); |
| |
| Dart_EnterIsolate(isolateA); |
| Dart_ShutdownIsolate(); |
| EXPECT_TRUE(IsolateIsShutdown(isolateA)); |
| EXPECT_FALSE(mgr.IsRegisteredForTestingOnly(isolateA)); |
| |
| Dart_EnterIsolate(isolateB); |
| Dart_ShutdownIsolate(); |
| EXPECT_TRUE(IsolateIsShutdown(isolateB)); |
| EXPECT_FALSE(mgr.IsRegisteredForTestingOnly(isolateB)); |
| |
| mgr.ShutdownPlatformIsolates(); |
| EXPECT_TRUE(mgr.HasShutdown()); |
| |
| EXPECT_TRUE(IsolateIsShutdown(isolateA)); |
| EXPECT_FALSE(mgr.IsRegisteredForTestingOnly(isolateA)); |
| EXPECT_TRUE(IsolateIsShutdown(isolateB)); |
| EXPECT_FALSE(mgr.IsRegisteredForTestingOnly(isolateB)); |
| }); |
| } |
| |
| TEST_F(PlatformIsolateManagerTest, RegistrationAfterShutdown) { |
| TestWithRootIsolate([this]() { |
| PlatformIsolateManager mgr; |
| EXPECT_FALSE(mgr.HasShutdown()); |
| |
| Dart_Isolate isolateA = CreateAndRegisterIsolate(&mgr); |
| ASSERT_TRUE(isolateA); |
| EXPECT_FALSE(IsolateIsShutdown(isolateA)); |
| EXPECT_TRUE(IsolateWasRegistered(isolateA)); |
| EXPECT_TRUE(mgr.IsRegisteredForTestingOnly(isolateA)); |
| |
| mgr.ShutdownPlatformIsolates(); |
| EXPECT_TRUE(mgr.HasShutdown()); |
| |
| EXPECT_TRUE(IsolateIsShutdown(isolateA)); |
| EXPECT_FALSE(mgr.IsRegisteredForTestingOnly(isolateA)); |
| |
| Dart_Isolate isolateB = CreateAndRegisterIsolate(&mgr); |
| ASSERT_TRUE(isolateB); |
| EXPECT_FALSE(IsolateIsShutdown(isolateB)); |
| EXPECT_FALSE(IsolateWasRegistered(isolateB)); |
| EXPECT_FALSE(mgr.IsRegisteredForTestingOnly(isolateB)); |
| |
| Dart_EnterIsolate(isolateB); |
| Dart_ShutdownIsolate(); |
| EXPECT_TRUE(IsolateIsShutdown(isolateB)); |
| EXPECT_FALSE(mgr.IsRegisteredForTestingOnly(isolateB)); |
| }); |
| } |
| |
| TEST_F(PlatformIsolateManagerTest, MultithreadedCreation) { |
| // Try to generate race conditions by creating Isolates on multiple threads, |
| // while shutting down the manager. |
| TestWithRootIsolate([this]() { |
| PlatformIsolateManager mgr; |
| EXPECT_FALSE(mgr.HasShutdown()); |
| |
| std::atomic<bool> test_finished = false; |
| std::vector<std::thread> threads; |
| threads.reserve(10); |
| for (int i = 0; i < 10; ++i) { |
| threads.push_back(std::thread([this, &mgr, &test_finished]() { |
| for (int j = 0; j < 100; ++j) { |
| Dart_Isolate isolate = CreateAndRegisterIsolate(&mgr); |
| ASSERT_TRUE(isolate); |
| |
| if (!IsolateWasRegistered(isolate)) { |
| Dart_EnterIsolate(isolate); |
| Dart_ShutdownIsolate(); |
| } |
| } |
| while (!test_finished.load()) { |
| // Wait for the test to finish, to avoid prematurely destroying thread |
| // local isolate_data_map_. |
| } |
| })); |
| } |
| |
| mgr.ShutdownPlatformIsolates(); |
| EXPECT_TRUE(mgr.HasShutdown()); |
| |
| test_finished = true; |
| for (auto& thread : threads) { |
| thread.join(); |
| } |
| }); |
| } |
| |
| } // namespace testing |
| } // namespace flutter |