• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "ecmascript/runtime.h"
17 
18 #include "ecmascript/checkpoint/thread_state_transition.h"
19 #include "ecmascript/jit/jit.h"
20 #include "ecmascript/jspandafile/program_object.h"
21 #include "ecmascript/mem/heap-inl.h"
22 namespace panda::ecmascript {
23 using PGOProfilerManager = pgo::PGOProfilerManager;
24 
25 int32_t Runtime::vmCount_ = 0;
26 int32_t Runtime::destroyCount_ = 0;
27 bool Runtime::firstVmCreated_ = false;
28 Mutex *Runtime::vmCreationLock_ = new Mutex();
29 Runtime *Runtime::instance_ = nullptr;
30 
GetInstance()31 Runtime *Runtime::GetInstance()
32 {
33     ASSERT(instance_ != nullptr);
34     return instance_;
35 }
36 
~Runtime()37 Runtime::~Runtime()
38 {
39     LockHolder lock(constpoolLock_);
40     auto iter = globalSharedConstpools_.begin();
41     while (iter != globalSharedConstpools_.end()) {
42         LOG_ECMA(INFO) << "remove js pandafile by vm destruct, file: " << iter->first->GetJSPandaFileDesc();
43         JSPandaFileManager::GetInstance()->RemoveJSPandaFile(iter->first);
44         iter->second.clear();
45         iter++;
46     }
47     globalSharedConstpools_.clear();
48     delete externalRegisteredStringTable_;
49     externalRegisteredStringTable_ = nullptr;
50 }
51 
CreateIfFirstVm(const JSRuntimeOptions & options)52 void Runtime::CreateIfFirstVm(const JSRuntimeOptions &options)
53 {
54     LockHolder lock(*vmCreationLock_);
55     if (!firstVmCreated_) {
56         Log::Initialize(options);
57         EcmaVM::InitializeIcuData(options);
58         MemMapAllocator::GetInstance()->Initialize(ecmascript::DEFAULT_REGION_SIZE);
59         PGOProfilerManager::GetInstance()->Initialize(options.GetPGOProfilerPath(),
60                                                       options.GetPGOHotnessThreshold());
61         ASSERT(instance_ == nullptr);
62         instance_ = new Runtime();
63         SharedHeap::CreateNewInstance();
64         DaemonThread::CreateNewInstance();
65         firstVmCreated_ = true;
66     }
67 }
68 
InitializeIfFirstVm(EcmaVM * vm)69 void Runtime::InitializeIfFirstVm(EcmaVM *vm)
70 {
71     {
72         LockHolder lock(*vmCreationLock_);
73         if (++vmCount_ == 1) {
74             ThreadManagedScope managedScope(vm->GetAssociatedJSThread());
75             PreInitialization(vm);
76             bool isEnableFastJit = vm->GetJSOptions().IsEnableJIT() && vm->GetJSOptions().GetEnableAsmInterpreter();
77             bool isEnableBaselineJit =
78                 vm->GetJSOptions().IsEnableBaselineJIT() && vm->GetJSOptions().GetEnableAsmInterpreter();
79             Jit::GetInstance()->SetEnableOrDisable(vm->GetJSOptions(), isEnableFastJit, isEnableBaselineJit);
80             vm->Initialize();
81             PostInitialization(vm);
82         }
83     }
84     if (!vm->IsInitialized()) {
85         ThreadManagedScope managedScope(vm->GetAssociatedJSThread());
86         vm->Initialize();
87     }
88 }
89 
PreInitialization(const EcmaVM * vm)90 void Runtime::PreInitialization(const EcmaVM *vm)
91 {
92     mainThread_ = vm->GetAssociatedJSThread();
93     mainThread_->SetMainThread();
94     nativeAreaAllocator_ = std::make_unique<NativeAreaAllocator>();
95     heapRegionAllocator_ = std::make_unique<HeapRegionAllocator>();
96     stringTable_ = std::make_unique<EcmaStringTable>();
97     SharedHeap::GetInstance()->Initialize(nativeAreaAllocator_.get(), heapRegionAllocator_.get(),
98         const_cast<EcmaVM*>(vm)->GetJSOptions(), DaemonThread::GetInstance());
99 }
100 
PostInitialization(const EcmaVM * vm)101 void Runtime::PostInitialization(const EcmaVM *vm)
102 {
103     // Use the main thread's globalconst after it has initialized,
104     // and copy shared parts to other thread's later.
105     sharedConstInited_ = true;
106     globalEnv_ = vm->GetGlobalEnv().GetTaggedValue();
107     globalConst_.CopySharedConstantsFrom(mainThread_->GlobalConstants());
108     SharedHeap::GetInstance()->PostInitialization(&globalConst_, const_cast<EcmaVM*>(vm)->GetJSOptions());
109     SharedModuleManager::GetInstance()->Initialize();
110 }
111 
DestroyIfLastVm()112 void Runtime::DestroyIfLastVm()
113 {
114     LockHolder lock(*vmCreationLock_);
115     if (--vmCount_ <= 0) {
116         Jit::GetInstance()->Destroy();
117         SharedModuleManager::GetInstance()->SharedNativeObjDestory();
118         SharedHeap::GetInstance()->WaitAllTasksFinishedAfterAllJSThreadEliminated();
119         DaemonThread::DestroyInstance();
120         SharedHeap::DestroyInstance();
121         AnFileDataManager::GetInstance()->SafeDestroyAllData();
122         MemMapAllocator::GetInstance()->Finalize();
123         PGOProfilerManager::GetInstance()->Destroy();
124         SharedModuleManager::GetInstance()->Destroy();
125         ASSERT(instance_ != nullptr);
126         delete instance_;
127         instance_ = nullptr;
128         firstVmCreated_ = false;
129     }
130 }
131 
RegisterThread(JSThread * newThread)132 void Runtime::RegisterThread(JSThread* newThread)
133 {
134     LockHolder lock(threadsLock_);
135     ASSERT(std::find(threads_.begin(), threads_.end(), newThread) == threads_.end());
136     threads_.emplace_back(newThread);
137     // send all current suspended requests to the new thread
138     for (uint32_t i = 0; i < suspendNewCount_; i++) {
139         newThread->SuspendThread(true);
140     }
141 }
142 
143 // Note: currently only called when thread is to be destroyed.
UnregisterThread(JSThread * thread)144 void Runtime::UnregisterThread(JSThread* thread)
145 {
146     LockHolder lock(threadsLock_);
147     ASSERT(std::find(threads_.begin(), threads_.end(), thread) != threads_.end());
148     ASSERT(!thread->IsInRunningState());
149     threads_.remove(thread);
150 }
151 
SuspendAll(JSThread * current)152 void Runtime::SuspendAll(JSThread *current)
153 {
154     ASSERT(current != nullptr);
155     ASSERT(!current->IsInRunningState());
156 #ifndef NDEBUG
157     ASSERT(!current->HasLaunchedSuspendAll());
158     current->LaunchSuspendAll();
159 #endif
160     SuspendAllThreadsImpl(current);
161 }
162 
ResumeAll(JSThread * current)163 void Runtime::ResumeAll(JSThread *current)
164 {
165     ASSERT(current != nullptr);
166     ASSERT(!current->IsInRunningState());
167 #ifndef NDEBUG
168     ASSERT(current->HasLaunchedSuspendAll());
169     current->CompleteSuspendAll();
170 #endif
171     ResumeAllThreadsImpl(current);
172 }
173 
SuspendAllThreadsImpl(JSThread * current)174 void Runtime::SuspendAllThreadsImpl(JSThread *current)
175 {
176     SuspendBarrier barrier;
177     for (uint32_t iterCount = 1U;; ++iterCount) {
178         {
179             LockHolder lock(threadsLock_);
180             if (suspendNewCount_ == 0) {
181                 suspendNewCount_++;
182                 if (threads_.size() == 1) {
183                     ASSERT(current == mainThread_ || current->IsDaemonThread());
184                     return;
185                 }
186                 if (threads_.size() < 1) {
187                     return;
188                 }
189                 barrier.Initialize(threads_.size() - 1);
190                 for (const auto& thread: threads_) {
191                     if (thread == current) {
192                         continue;
193                     }
194                     thread->SuspendThread(+1, &barrier);
195                     // The two flags, SUSPEND_REQUEST and ACTIVE_BARRIER, are set by Suspend-Thread guarded by
196                     // suspendLock_, so the target thread-I may do not see these two flags in time. As a result, it
197                     // can switch its state freely without responding to the ACTIVE_BARRIER flag and the
198                     // suspend-thread will always wait it. However, as long as it sees the flags, the actions of
199                     // passing barrier will be triggered. When the thread-I switches from NON_RUNNING to RUNNING,
200                     // it will firstly pass the barrier and then be blocked by the SUSPEND_REQUEST flag. If the
201                     // thread-I switches from RUNNING to NON_RUNNING, it will switch the state and then act on the
202                     // barrier. If the thread-I go to checkpoint in RUNNING state, it will act on the barrier
203                     // and be blocked by SUSPEND_REQUEST flag.
204                     if (thread->IsSuspended()) {
205                         // Because of the multi-threads situation, currently thread-I may be in RUNNING state or is
206                         // goding to be RUNNING state even inside this branch. In both scenarios, for instance of
207                         // RUNNING state, according to the modification order of atomic-variable stateAndFlags_,
208                         // thread-I will see the SUSPEND_REQUEST and ACTIVE_BARRIER and act on them before switching
209                         // to RUNNING. Besides, notice the using of suspendLock_ inside PassSuspendBarrier(), there
210                         // is not data-race for passing barrier.
211                         thread->PassSuspendBarrier();
212                     }
213                 }
214                 break;
215             }
216         }
217         if (iterCount < MAX_SUSPEND_RETRIES) {
218             LockHolder lock(threadsLock_);
219             if (suspendNewCount_ != 0) {
220                 // Someone has already suspended all threads.
221                 // Wait until it finishes.
222                 threadSuspendCondVar_.Wait(&threadsLock_);
223             }
224         } else {
225             LOG_ECMA(FATAL) << "Too many SuspendAll retries!";
226             UNREACHABLE();
227         }
228     }
229     barrier.Wait();
230 }
231 
ResumeAllThreadsImpl(JSThread * current)232 void Runtime::ResumeAllThreadsImpl(JSThread *current)
233 {
234     LockHolder lock(threadsLock_);
235     if (suspendNewCount_ > 0) {
236         suspendNewCount_--;
237     }
238     if (suspendNewCount_ == 0) {
239         // Signal to waiting to suspend threads
240         threadSuspendCondVar_.Signal();
241     }
242     for (const auto& thread : threads_) {
243         if (thread != current) {
244             thread->ResumeThread(true);
245         }
246     }
247 }
248 
IterateSharedRoot(RootVisitor & visitor)249 void Runtime::IterateSharedRoot(RootVisitor &visitor)
250 {
251     IterateSerializeRoot(visitor);
252     SharedModuleManager::GetInstance()->Iterate(visitor);
253     IterateCachedStringRoot(visitor);
254 }
255 
IterateSerializeRoot(RootVisitor & v)256 void Runtime::IterateSerializeRoot(RootVisitor &v)
257 {
258     LockHolder lock(serializeLock_);
259     for (auto &it : serializeRootMap_) {
260         it.second->Iterate(v);
261     }
262 }
263 
HasCachedConstpool(const JSPandaFile * jsPandaFile)264 bool Runtime::HasCachedConstpool(const JSPandaFile *jsPandaFile)
265 {
266     LockHolder lock(constpoolLock_);
267     return globalSharedConstpools_.find(jsPandaFile) != globalSharedConstpools_.end();
268 }
269 
FindConstpool(const JSPandaFile * jsPandaFile,int32_t index)270 JSTaggedValue Runtime::FindConstpool(const JSPandaFile *jsPandaFile, int32_t index)
271 {
272     LockHolder lock(constpoolLock_);
273     return FindConstpoolUnlocked(jsPandaFile, index);
274 }
275 
FindConstpoolUnlocked(const JSPandaFile * jsPandaFile,int32_t index)276 JSTaggedValue Runtime::FindConstpoolUnlocked(const JSPandaFile *jsPandaFile, int32_t index)
277 {
278     auto iter = globalSharedConstpools_.find(jsPandaFile);
279     if (iter == globalSharedConstpools_.end()) {
280         return JSTaggedValue::Hole();
281     }
282     auto constpoolIter = iter->second.find(index);
283     if (constpoolIter == iter->second.end()) {
284         return JSTaggedValue::Hole();
285     }
286     return constpoolIter->second;
287 }
288 
AddOrUpdateConstpool(const JSPandaFile * jsPandaFile,JSHandle<ConstantPool> constpool,int32_t index)289 JSHandle<ConstantPool> Runtime::AddOrUpdateConstpool(const JSPandaFile *jsPandaFile,
290                                                      JSHandle<ConstantPool> constpool,
291                                                      int32_t index)
292 {
293     LockHolder lock(constpoolLock_);
294     if (globalSharedConstpools_.find(jsPandaFile) == globalSharedConstpools_.end()) {
295         globalSharedConstpools_[jsPandaFile] = CMap<int32_t, JSTaggedValue>();
296     }
297     auto &constpoolMap = globalSharedConstpools_[jsPandaFile];
298     auto iter = constpoolMap.find(index);
299     if (iter == constpoolMap.end()) {
300         int32_t constpoolIndex = GetAndIncreaseSharedConstpoolCount();
301         constpool->SetUnsharedConstpoolIndex(JSTaggedValue(constpoolIndex));
302         constpoolMap.insert({index, constpool.GetTaggedValue()});
303         return constpool;
304     }
305     return JSHandle<ConstantPool>(reinterpret_cast<uintptr_t>(&iter->second));
306 }
307 
FindConstpools(const JSPandaFile * jsPandaFile)308 std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> Runtime::FindConstpools(
309     const JSPandaFile *jsPandaFile)
310 {
311     LockHolder lock(constpoolLock_);
312     auto iter = globalSharedConstpools_.find(jsPandaFile);
313     if (iter == globalSharedConstpools_.end()) {
314         return std::nullopt;
315     }
316     return iter->second;
317 }
318 
ProcessNativeDeleteInSharedGC(const WeakRootVisitor & visitor)319 void Runtime::ProcessNativeDeleteInSharedGC(const WeakRootVisitor &visitor)
320 {
321     // No need lock here, only shared gc will sweep shared constpool, meanwhile other threads are suspended.
322     auto iterator = globalSharedConstpools_.begin();
323     ECMA_BYTRACE_NAME(HITRACE_TAG_ARK, "Constpools:" + std::to_string(globalSharedConstpools_.size()));
324     while (iterator != globalSharedConstpools_.end()) {
325         auto &constpools = iterator->second;
326         auto constpoolIter = constpools.begin();
327         while (constpoolIter != constpools.end()) {
328             JSTaggedValue constpoolVal = constpoolIter->second;
329             if (constpoolVal.IsHeapObject()) {
330                 TaggedObject *obj = constpoolVal.GetTaggedObject();
331                 auto fwd = visitor(obj);
332                 if (fwd == nullptr) {
333                     int32_t constpoolIndex =
334                         ConstantPool::Cast(constpoolVal.GetTaggedObject())->GetUnsharedConstpoolIndex();
335                     EraseUnusedConstpool(iterator->first, constpoolIter->first, constpoolIndex);
336                     constpoolIter = constpools.erase(constpoolIter);
337                     // when shared constpool is not referenced by any objects,
338                     // global unshared constpool count can be reuse.
339                     freeSharedConstpoolIndex_.insert(constpoolIndex);
340                     continue;
341                 }
342                 if (fwd != reinterpret_cast<TaggedObject *>(obj)) {
343                     constpoolIter->second = JSTaggedValue(fwd);
344                 }
345             }
346             ++constpoolIter;
347         }
348         if (constpools.size() == 0) {
349             LOG_ECMA(INFO) << "remove js pandafile by gc, file:" << iterator->first->GetJSPandaFileDesc();
350             JSPandaFileManager::GetInstance()->RemoveJSPandaFile(iterator->first);
351             iterator = globalSharedConstpools_.erase(iterator);
352         } else {
353             ++iterator;
354         }
355     }
356 }
357 
EraseUnusedConstpool(const JSPandaFile * jsPandaFile,int32_t index,int32_t constpoolIndex)358 void Runtime::EraseUnusedConstpool(const JSPandaFile *jsPandaFile, int32_t index, int32_t constpoolIndex)
359 {
360     GCIterateThreadList([jsPandaFile, index, constpoolIndex](JSThread* thread) {
361         ASSERT(!thread->IsInRunningState());
362         auto context = thread->GetCurrentEcmaContext();
363         context->EraseUnusedConstpool(jsPandaFile, index, constpoolIndex);
364     });
365 }
366 
ProcessSharedNativeDelete(const WeakRootVisitor & visitor)367 void Runtime::ProcessSharedNativeDelete(const WeakRootVisitor &visitor)
368 {
369     SharedHeap::GetInstance()->ProcessSharedNativeDelete(visitor);
370 }
371 
PushToSharedNativePointerList(JSNativePointer * pointer)372 void Runtime::PushToSharedNativePointerList(JSNativePointer *pointer)
373 {
374     SharedHeap::GetInstance()->PushToSharedNativePointerList(pointer);
375 }
376 
InvokeSharedNativePointerCallbacks()377 void Runtime::InvokeSharedNativePointerCallbacks()
378 {
379     auto &callbacks = GetSharedNativePointerCallbacks();
380     while (!callbacks.empty()) {
381         auto callbackPair = callbacks.back();
382         callbacks.pop_back();
383         ASSERT(callbackPair.first != nullptr && callbackPair.second.first != nullptr &&
384                callbackPair.second.second != nullptr);
385         auto callback = callbackPair.first;
386         (*callback)(nullptr, callbackPair.second.first, callbackPair.second.second);
387     }
388 }
389 }  // namespace panda::ecmascript
390