blob: 312beff516999cd01a55da6426698ca59f13273e [file] [log] [blame]
// 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