Skip to content

Commit

Permalink
Shut down and restart the Dart VM as needed.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
chinmaygarde committed Jan 9, 2019
1 parent 26e02aa commit fb5c35a
Show file tree
Hide file tree
Showing 8 changed files with 152 additions and 80 deletions.
1 change: 1 addition & 0 deletions runtime/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ executable("runtime_unittests") {
":runtime",
":runtime_fixtures",
"$flutter_root/fml",
"$flutter_root/shell/common",
"$flutter_root/lib/snapshot",
"$flutter_root/testing",
"//third_party/dart/runtime:libdart_jit",
Expand Down
25 changes: 10 additions & 15 deletions runtime/dart_isolate_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,6 @@
#include "flutter/testing/testing.h"
#include "flutter/testing/thread_test.h"

#define CURRENT_TEST_NAME \
std::string { \
::testing::UnitTest::GetInstance()->current_test_info()->name() \
}

namespace blink {

using DartIsolateTest = ::testing::ThreadTest;
Expand All @@ -23,11 +18,11 @@ TEST_F(DartIsolateTest, RootIsolateCreationAndShutdown) {
settings.task_observer_remove = [](intptr_t) {};
auto vm = DartVM::ForProcess(settings);
ASSERT_TRUE(vm);
TaskRunners task_runners(CURRENT_TEST_NAME, //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner() //
TaskRunners task_runners(testing::GetCurrentTestName(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner() //
);
auto weak_isolate = DartIsolate::CreateRootIsolate(
vm.get(), // vm
Expand All @@ -53,11 +48,11 @@ TEST_F(DartIsolateTest, IsolateShutdownCallbackIsInIsolateScope) {
settings.task_observer_remove = [](intptr_t) {};
auto vm = DartVM::ForProcess(settings);
ASSERT_TRUE(vm);
TaskRunners task_runners(CURRENT_TEST_NAME, //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner() //
TaskRunners task_runners(testing::GetCurrentTestName(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner(), //
GetCurrentTaskRunner() //
);
auto weak_isolate = DartIsolate::CreateRootIsolate(
vm.get(), // vm
Expand Down
98 changes: 54 additions & 44 deletions runtime/dart_vm.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -233,54 +234,63 @@ static void EmbedderInformationCallback(Dart_EmbedderInformation* info) {
info->name = "Flutter";
}

fml::RefPtr<DartVM> DartVM::ForProcess(Settings settings) {
std::shared_ptr<DartVM> DartVM::ForProcess(Settings settings) {
return ForProcess(settings, nullptr, nullptr, nullptr);
}

static std::once_flag gVMInitialization;
static std::mutex gVMMutex;
static fml::RefPtr<DartVM> gVM;
static std::weak_ptr<DartVM> gVM;

fml::RefPtr<DartVM> DartVM::ForProcess(
std::shared_ptr<DartVM> DartVM::ForProcess(
Settings settings,
fml::RefPtr<DartSnapshot> vm_snapshot,
fml::RefPtr<DartSnapshot> isolate_snapshot,
fml::RefPtr<DartSnapshot> shared_snapshot) {
std::lock_guard<std::mutex> 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<DartVM>(settings, //
std::move(vm_snapshot), //
std::move(isolate_snapshot), //
std::move(shared_snapshot) //
);
});
return gVM;

// TODO(chinmaygarde): Make this an assertion instead so that callers are
// notified that the new settings might not hold because an exiting VM was
// being reused.
if (auto vm = gVM.lock()) {
return vm;
}

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();
}

// Note: std::make_shared unviable due to hidden constructor.
auto vm = std::shared_ptr<DartVM>(new DartVM(settings, //
std::move(vm_snapshot), //
std::move(isolate_snapshot), //
std::move(shared_snapshot) //
));

gVM = vm;

return vm;
}

fml::RefPtr<DartVM> DartVM::ForProcessIfInitialized() {
std::lock_guard<std::mutex> lock(gVMMutex);
return gVM;
std::shared_ptr<DartVM> DartVM::ForProcessIfInitialized() {
return gVM.lock();
}

DartVM::DartVM(const Settings& settings,
Expand All @@ -290,8 +300,7 @@ DartVM::DartVM(const Settings& settings,
: settings_(settings),
vm_snapshot_(std::move(vm_snapshot)),
isolate_snapshot_(std::move(isolate_snapshot)),
shared_snapshot_(std::move(shared_snapshot)),
weak_factory_(this) {
shared_snapshot_(std::move(shared_snapshot)) {
TRACE_EVENT0("flutter", "DartVMInitializer");
FML_DLOG(INFO) << "Attempting Dart VM launch for mode: "
<< (IsRunningPrecompiledCode() ? "AOT" : "Interpreter");
Expand Down Expand Up @@ -381,8 +390,6 @@ DartVM::DartVM(const Settings& settings,

DartUI::InitForGlobal();

Dart_SetFileModifiedCallback(&DartFileModifiedCallback);

{
TRACE_EVENT0("flutter", "Dart_Initialize");
Dart_InitializeParams params = {};
Expand Down Expand Up @@ -420,6 +427,8 @@ DartVM::DartVM(const Settings& settings,
}
}

Dart_SetFileModifiedCallback(&DartFileModifiedCallback);

// Allow streaming of stdout and stderr by the Dart vm.
Dart_SetServiceStreamCallbacks(&ServiceStreamListenCallback,
&ServiceStreamCancelCallback);
Expand All @@ -428,10 +437,15 @@ DartVM::DartVM(const Settings& settings,
}

DartVM::~DartVM() {
std::lock_guard<std::mutex> lock(gVMMutex);
if (Dart_CurrentIsolate() != nullptr) {
Dart_ExitIsolate();
}

char* result = Dart_Cleanup();

dart::bin::CleanupDartIo();

if (result != nullptr) {
FML_LOG(ERROR) << "Could not cleanly shut down the Dart VM. Message: \""
<< result << "\".";
Expand Down Expand Up @@ -463,8 +477,4 @@ ServiceProtocol& DartVM::GetServiceProtocol() {
return service_protocol_;
}

fml::WeakPtr<DartVM> DartVM::GetWeakPtr() {
return weak_factory_.GetWeakPtr();
}

} // namespace blink
18 changes: 7 additions & 11 deletions runtime/dart_vm.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#define FLUTTER_RUNTIME_DART_VM_H_

#include <functional>
#include <memory>
#include <string>
#include <vector>

Expand All @@ -24,17 +25,19 @@

namespace blink {

class DartVM : public fml::RefCountedThreadSafe<DartVM> {
class DartVM {
public:
static fml::RefPtr<DartVM> ForProcess(Settings settings);
~DartVM();

static std::shared_ptr<DartVM> ForProcess(Settings settings);

static fml::RefPtr<DartVM> ForProcess(
static std::shared_ptr<DartVM> ForProcess(
Settings settings,
fml::RefPtr<DartSnapshot> vm_snapshot,
fml::RefPtr<DartSnapshot> isolate_snapshot,
fml::RefPtr<DartSnapshot> shared_snapshot);

static fml::RefPtr<DartVM> ForProcessIfInitialized();
static std::shared_ptr<DartVM> ForProcessIfInitialized();

static bool IsRunningPrecompiledCode();

Expand All @@ -50,8 +53,6 @@ class DartVM : public fml::RefCountedThreadSafe<DartVM> {

fml::RefPtr<DartSnapshot> GetSharedSnapshot() const;

fml::WeakPtr<DartVM> GetWeakPtr();

ServiceProtocol& GetServiceProtocol();

private:
Expand All @@ -61,17 +62,12 @@ class DartVM : public fml::RefCountedThreadSafe<DartVM> {
const fml::RefPtr<DartSnapshot> isolate_snapshot_;
const fml::RefPtr<DartSnapshot> shared_snapshot_;
ServiceProtocol service_protocol_;
fml::WeakPtrFactory<DartVM> weak_factory_;

DartVM(const Settings& settings,
fml::RefPtr<DartSnapshot> vm_snapshot,
fml::RefPtr<DartSnapshot> isolate_snapshot,
fml::RefPtr<DartSnapshot> shared_snapshot);

~DartVM();

FML_FRIEND_REF_COUNTED_THREAD_SAFE(DartVM);
FML_FRIEND_MAKE_REF_COUNTED(DartVM);
FML_DISALLOW_COPY_AND_ASSIGN(DartVM);
};

Expand Down
80 changes: 72 additions & 8 deletions runtime/dart_vm_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,29 @@
// found in the LICENSE file.

#include "flutter/runtime/dart_vm.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.task_observer_add = [](intptr_t, fml::closure) {};
settings.task_observer_remove = [](intptr_t) {};
auto vm = DartVM::ForProcess(settings);
return settings;
}

TEST(DartVM, SimpleInitialization) {
auto vm = DartVM::ForProcess(GetTestSettings());
ASSERT_TRUE(vm);
ASSERT_EQ(vm, DartVM::ForProcess(settings));
ASSERT_EQ(vm, DartVM::ForProcess(GetTestSettings()));
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 = DartVM::ForProcess(GetTestSettings());
auto ns = vm->GetIsolateNameServer();
ASSERT_EQ(ns->LookupIsolatePortByName("foobar"), ILLEGAL_PORT);
ASSERT_FALSE(ns->RemoveIsolateNameMapping("foobar"));
Expand All @@ -31,4 +35,64 @@ TEST(DartVM, SimpleIsolateNameServer) {
ASSERT_TRUE(ns->RemoveIsolateNameMapping("foobar"));
}

TEST(DartVM, CanReinitializeVMOverAndOver) {
for (size_t i = 0; i < 1000; ++i) {
FML_LOG(INFO) << "Run " << i + 1;
// VM should not already be running.
ASSERT_FALSE(DartVM::ForProcessIfInitialized());
auto vm = DartVM::ForProcess(GetTestSettings());
ASSERT_TRUE(vm);
ASSERT_TRUE(DartVM::ForProcessIfInitialized());
}
}

using DartVMThreadTest = ::testing::ThreadTest;

TEST_F(DartVMThreadTest, CanRunIsolatesInANewVM) {
for (size_t i = 0; i < 1000; ++i) {
FML_LOG(INFO) << "Run " << i + 1;
// VM should not already be running.
ASSERT_FALSE(DartVM::ForProcessIfInitialized());
auto vm = DartVM::ForProcess(GetTestSettings());
ASSERT_TRUE(vm);
ASSERT_TRUE(DartVM::ForProcessIfInitialized());

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.get(), // vm
vm->GetIsolateSnapshot(), // isolate snapshot
vm->GetSharedSnapshot(), // shared snapshot
std::move(task_runners), // task runners
nullptr, // window
{}, // snapshot delegate
{}, // resource context
nullptr, // unref qeueue
"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
2 changes: 1 addition & 1 deletion shell/common/shell.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class Shell final : public PlatformView::Delegate,

const blink::TaskRunners task_runners_;
const blink::Settings settings_;
fml::RefPtr<blink::DartVM> vm_;
std::shared_ptr<blink::DartVM> vm_;
std::unique_ptr<PlatformView> platform_view_; // on platform task runner
std::unique_ptr<Engine> engine_; // on UI task runner
std::unique_ptr<Rasterizer> rasterizer_; // on GPU task runner
Expand Down
4 changes: 3 additions & 1 deletion testing/testing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

namespace testing {

//
std::string GetCurrentTestName() {
return UnitTest::GetInstance()->current_test_info()->name();
}

} // namespace testing
Loading

0 comments on commit fb5c35a

Please sign in to comment.