1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/runtime/dart_vm_lifecycle.h"
6
7#include <mutex>
8
9namespace flutter {
10
11// We need to explicitly put the constructor and destructor of the DartVM in the
12// critical section. All accesses (not just const members) to the global VM
13// object weak pointer are behind this mutex.
14static std::mutex gVMMutex;
15static std::weak_ptr<DartVM> gVM;
16static std::shared_ptr<DartVM>* gVMLeak;
17
18// We are going to be modifying more than just the control blocks of the
19// following weak pointers (in the |Create| case where an old VM could not be
20// reused). Ideally, we would use |std::atomic<std::weak_ptr<T>>| specialization
21// but that is only available since C++20. We don't expect contention on these
22// locks so we just use one mutex for all.
23static std::mutex gVMDependentsMutex;
24static std::weak_ptr<const DartVMData> gVMData;
25static std::weak_ptr<ServiceProtocol> gVMServiceProtocol;
26static std::weak_ptr<IsolateNameServer> gVMIsolateNameServer;
27
28DartVMRef::DartVMRef(std::shared_ptr<DartVM> vm) : vm_(vm) {}
29
30DartVMRef::DartVMRef(DartVMRef&& other) = default;
31
32DartVMRef::~DartVMRef() {
33 if (!vm_) {
34 // If there is no valid VM (possible via a move), there is no way that the
35 // decrement on the shared pointer can cause a collection. Avoid acquiring
36 // the lifecycle lock in this case. This is just working around a
37 // pessimization and not required for correctness.
38 return;
39 }
40 std::scoped_lock lifecycle_lock(gVMMutex);
41 vm_.reset();
42}
43
44DartVMRef DartVMRef::Create(Settings settings,
45 fml::RefPtr<DartSnapshot> vm_snapshot,
46 fml::RefPtr<DartSnapshot> isolate_snapshot) {
47 std::scoped_lock lifecycle_lock(gVMMutex);
48
49 if (!settings.leak_vm) {
50 FML_CHECK(!gVMLeak)
51 << "Launch settings indicated that the VM should shut down in the "
52 "process when done but a previous launch asked the VM to leak in "
53 "the same process. For proper VM shutdown, all VM launches must "
54 "indicate that they should shut down when done.";
55 }
56
57 // If there is already a running VM in the process, grab a strong reference to
58 // it.
59 if (auto vm = gVM.lock()) {
60 FML_DLOG(WARNING) << "Attempted to create a VM in a process where one was "
61 "already running. Ignoring arguments for current VM "
62 "create call and reusing the old VM.";
63 // There was already a running VM in the process,
64 return DartVMRef{std::move(vm)};
65 }
66
67 std::scoped_lock dependents_lock(gVMDependentsMutex);
68
69 gVMData.reset();
70 gVMServiceProtocol.reset();
71 gVMIsolateNameServer.reset();
72 gVM.reset();
73
74 // If there is no VM in the process. Initialize one, hold the weak reference
75 // and pass a strong reference to the caller.
76 auto isolate_name_server = std::make_shared<IsolateNameServer>();
77 auto vm = DartVM::Create(std::move(settings), //
78 std::move(vm_snapshot), //
79 std::move(isolate_snapshot), //
80 isolate_name_server //
81 );
82
83 if (!vm) {
84 FML_LOG(ERROR) << "Could not create Dart VM instance.";
85 return {nullptr};
86 }
87
88 gVMData = vm->GetVMData();
89 gVMServiceProtocol = vm->GetServiceProtocol();
90 gVMIsolateNameServer = isolate_name_server;
91 gVM = vm;
92
93 if (settings.leak_vm) {
94 gVMLeak = new std::shared_ptr<DartVM>(vm);
95 }
96
97 return DartVMRef{std::move(vm)};
98}
99
100bool DartVMRef::IsInstanceRunning() {
101 std::scoped_lock lock(gVMMutex);
102 return !gVM.expired();
103}
104
105std::shared_ptr<const DartVMData> DartVMRef::GetVMData() {
106 std::scoped_lock lock(gVMDependentsMutex);
107 return gVMData.lock();
108}
109
110std::shared_ptr<ServiceProtocol> DartVMRef::GetServiceProtocol() {
111 std::scoped_lock lock(gVMDependentsMutex);
112 return gVMServiceProtocol.lock();
113}
114
115std::shared_ptr<IsolateNameServer> DartVMRef::GetIsolateNameServer() {
116 std::scoped_lock lock(gVMDependentsMutex);
117 return gVMIsolateNameServer.lock();
118}
119
120DartVM* DartVMRef::GetRunningVM() {
121 std::scoped_lock lock(gVMMutex);
122 auto vm = gVM.lock().get();
123 FML_CHECK(vm) << "Caller assumed VM would be running when it wasn't";
124 return vm;
125}
126
127} // namespace flutter
128