• 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 #ifndef ECMASCRIPT_RUNTIME_H
17 #define ECMASCRIPT_RUNTIME_H
18 
19 #include "ecmascript/ecma_string_table.h"
20 #include "ecmascript/global_env_constants.h"
21 #include "ecmascript/js_runtime_options.h"
22 #include "ecmascript/js_thread.h"
23 #include "ecmascript/mem/heap.h"
24 #include "ecmascript/module/js_shared_module_manager.h"
25 #include "ecmascript/mutator_lock.h"
26 #include "ecmascript/platform/mutex.h"
27 #include "ecmascript/serializer/serialize_chunk.h"
28 
29 #include "libpandabase/macros.h"
30 
31 #include <list>
32 #include <memory>
33 
34 namespace panda::ecmascript {
35 class Runtime {
36 public:
37     PUBLIC_API static Runtime *GetInstance();
38 
39     static void CreateIfFirstVm(const JSRuntimeOptions &options);
40     static void DestroyIfLastVm();
41     void InitializeIfFirstVm(EcmaVM *vm);
42 
43     void RegisterThread(JSThread* newThread);
44     void UnregisterThread(JSThread* thread);
45 
46     void SuspendAll(JSThread *current);
47     void ResumeAll(JSThread *current);
48     void IterateSerializeRoot(const RootVisitor &v);
49 
GetMainThread()50     JSThread *GetMainThread() const
51     {
52         return mainThread_;
53     }
54 
GetMutatorLock()55     MutatorLock *GetMutatorLock()
56     {
57         return &mutatorLock_;
58     }
59 
GetMutatorLock()60     const MutatorLock *GetMutatorLock() const
61     {
62         return &mutatorLock_;
63     }
64 
65     template<class Callback>
GCIterateThreadList(const Callback & cb)66     void GCIterateThreadList(const Callback &cb)
67     {
68         LockHolder lock(threadsLock_);
69         for (auto thread : threads_) {
70             if (thread->ReadyForGCIterating()) {
71                 cb(thread);
72             }
73         }
74     }
75 
76     // Result may be inaccurate, just an approximate value.
ApproximateThreadListSize()77     size_t ApproximateThreadListSize()
78     {
79         return threads_.size();
80     }
81 
GetGlobalEnvConstants()82     inline const GlobalEnvConstants *GetGlobalEnvConstants()
83     {
84         return &globalConst_;
85     }
86 
SharedConstInited()87     inline bool SharedConstInited()
88     {
89         return sharedConstInited_;
90     }
91 
GetGlobalEnv()92     JSTaggedValue GetGlobalEnv() const
93     {
94         return globalEnv_;
95     }
96 
GetEcmaStringTable()97     inline EcmaStringTable *GetEcmaStringTable() const
98     {
99         return stringTable_.get();
100     }
101 
GetSerializeRootMapValue(JSThread * thread,uint32_t dataIndex)102     inline SerializationChunk *GetSerializeRootMapValue([[maybe_unused]] JSThread *thread,
103         uint32_t dataIndex)
104     {
105         ASSERT(thread->IsInManagedState());
106         LockHolder lock(serializeLock_);
107         auto iter = serializeRootMap_.find(dataIndex);
108         if (iter == serializeRootMap_.end()) {
109             return nullptr;
110         }
111         return iter->second.get();
112     }
113 
PushSerializationRoot(JSThread * thread,std::unique_ptr<SerializationChunk> chunk)114     uint32_t PushSerializationRoot([[maybe_unused]] JSThread *thread, std::unique_ptr<SerializationChunk> chunk)
115     {
116         ASSERT(thread->IsInManagedState());
117         LockHolder lock(serializeLock_);
118         uint32_t index = GetSerializeDataIndex();
119         ASSERT(serializeRootMap_.find(index) == serializeRootMap_.end());
120         serializeRootMap_.emplace(index, std::move(chunk));
121         return index;
122     }
123 
RemoveSerializationRoot(JSThread * thread,uint32_t index)124     void RemoveSerializationRoot([[maybe_unused]] JSThread *thread, uint32_t index)
125     {
126         ASSERT(thread->IsInManagedState());
127         LockHolder lock(serializeLock_);
128         ASSERT(serializeRootMap_.find(index) != serializeRootMap_.end());
129         serializeRootMap_.erase(index);
130         serializeDataIndexVector_.emplace_back(index);
131     }
132 
SharedGCRequest()133     static bool SharedGCRequest()
134     {
135         LockHolder lock(*vmCreationLock_);
136         destroyCount_++;
137         if (destroyCount_ == WORKER_DESTRUCTION_COUNT || vmCount_ < MIN_GC_TRIGGER_VM_COUNT) {
138             destroyCount_ = 0;
139             return true;
140         } else {
141             return false;
142         }
143     }
144 
145     bool HasCachedConstpool(const JSPandaFile *jsPandaFile);
146     PUBLIC_API JSTaggedValue FindConstpool(const JSPandaFile *jsPandaFile, int32_t index);
147     JSTaggedValue FindConstpoolUnlocked(const JSPandaFile *jsPandaFile, int32_t index);
148     JSHandle<ConstantPool> AddOrUpdateConstpool(const JSPandaFile *jsPandaFile,
149                                                 JSHandle<ConstantPool> constpool,
150                                                 int32_t index = 0);
151     std::optional<std::reference_wrapper<CMap<int32_t, JSTaggedValue>>> FindConstpools(
152         const JSPandaFile *jsPandaFile);
153     void EraseUnusedConstpool(const JSPandaFile *jsPandaFile, int32_t index, int32_t constpoolIndex);
154 
155     void ProcessNativeDeleteInSharedGC(const WeakRootVisitor &visitor);
156 
CreateStringCacheTable(uint32_t size)157     inline bool CreateStringCacheTable(uint32_t size)
158     {
159         constexpr int32_t MAX_SIZE = 150;
160         if ((size == 0) || (size > MAX_SIZE) || (externalRegisteredStringTable_ != nullptr)) {
161             LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has been registered.";
162             LOG_ECMA(ERROR) << "Currently, maximum size of the table is " << MAX_SIZE;
163             return false;
164         }
165 
166         externalRegisteredStringTable_ = new JSTaggedValue[size];
167         if (externalRegisteredStringTable_ == nullptr) {
168             LOG_ECMA(ERROR) << "create string cache table failed";
169             return false;
170         }
171         registeredStringTableSize_ = size;
172         return true;
173     }
174 
SetCachedString(JSHandle<EcmaString> str,uint32_t propertyIndex)175     inline bool SetCachedString(JSHandle<EcmaString> str, uint32_t propertyIndex)
176     {
177         if (propertyIndex >= registeredStringTableSize_ || (externalRegisteredStringTable_ == nullptr)) {
178             LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered.";
179             return false;
180         }
181         externalRegisteredStringTable_[propertyIndex] = str.GetTaggedValue();
182         return true;
183     }
184 
GetCachedString(JSThread * thread,uint32_t propertyIndex)185     inline JSHandle<EcmaString> GetCachedString(JSThread *thread, uint32_t propertyIndex)
186     {
187         if ((externalRegisteredStringTable_ == nullptr) || (propertyIndex >= registeredStringTableSize_)) {
188             LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered.";
189             return JSHandle<EcmaString>(thread->GlobalConstants()->GetHandledEmptyString());
190         }
191         return JSHandle<EcmaString>(reinterpret_cast<uintptr_t>(&externalRegisteredStringTable_[propertyIndex]));
192     }
193 
HasCachedString(uint32_t propertyIndex)194     inline bool HasCachedString(uint32_t propertyIndex)
195     {
196         if ((externalRegisteredStringTable_ == nullptr) || propertyIndex >= registeredStringTableSize_) {
197             LOG_ECMA(ERROR) << "invalid size of the string cache table or the table has never been registered.";
198             return false;
199         }
200 
201         if (externalRegisteredStringTable_[propertyIndex].GetRawData() != JSTaggedValue::NULL_POINTER) {
202             return true;
203         }
204         return false;
205     }
206 
IterateCachedStringRoot(const RootRangeVisitor & v)207     void IterateCachedStringRoot(const RootRangeVisitor &v)
208     {
209         if ((externalRegisteredStringTable_ == nullptr) || (registeredStringTableSize_ <= 0)) {
210             return;
211         }
212         auto begin = ObjectSlot(reinterpret_cast<uintptr_t>(externalRegisteredStringTable_));
213         auto end = ObjectSlot(reinterpret_cast<uintptr_t>(externalRegisteredStringTable_ +
214             registeredStringTableSize_));
215         v(Root::ROOT_VM, begin, end);
216     }
217 
218 private:
219     static constexpr int32_t WORKER_DESTRUCTION_COUNT = 3;
220     static constexpr int32_t MIN_GC_TRIGGER_VM_COUNT = 4;
221     static constexpr uint32_t MAX_SUSPEND_RETRIES = 5000;
222     Runtime() = default;
223     ~Runtime();
224     void SuspendAllThreadsImpl(JSThread *current);
225     void ResumeAllThreadsImpl(JSThread *current);
226 
227     void PreInitialization(const EcmaVM *vm);
228     void PostInitialization(const EcmaVM *vm);
229 
GetSerializeDataIndex()230     uint32_t GetSerializeDataIndex()
231     {
232         if (!serializeDataIndexVector_.empty()) {
233             uint32_t index = serializeDataIndexVector_.back();
234             serializeDataIndexVector_.pop_back();
235             return index;
236         }
237         return ++serializeDataIndex_;
238     }
239 
GetAndIncreaseSharedConstpoolCount()240     int32_t GetAndIncreaseSharedConstpoolCount()
241     {
242         if (freeSharedConstpoolIndex_.size() > 0) {
243             auto iter = freeSharedConstpoolIndex_.begin();
244             int32_t freeCount = *iter;
245             freeSharedConstpoolIndex_.erase(iter);
246             return freeCount;
247         }
248         return sharedConstpoolCount_++;
249     }
250 
251     Mutex threadsLock_;
252     ConditionVariable threadSuspendCondVar_;
253     Mutex serializeLock_;
254     std::list<JSThread*> threads_;
255     uint32_t suspendNewCount_ {0};
256     uint32_t serializeDataIndex_ {0};
257     MutatorLock mutatorLock_;
258 
259     bool sharedConstInited_ {false};
260     GlobalEnvConstants globalConst_;
261     JSTaggedValue globalEnv_ {JSTaggedValue::Hole()};
262     JSThread *mainThread_ {nullptr};
263     // for shared heap.
264     std::unique_ptr<NativeAreaAllocator> nativeAreaAllocator_;
265     std::unique_ptr<HeapRegionAllocator> heapRegionAllocator_;
266     // for stringTable.
267     std::unique_ptr<EcmaStringTable> stringTable_;
268     std::unordered_map<uint32_t, std::unique_ptr<SerializationChunk>> serializeRootMap_;
269     std::vector<uint32_t> serializeDataIndexVector_;
270 
271     // Shared constantpool cache
272     Mutex constpoolLock_;
273     CMap<const JSPandaFile *, CMap<int32_t, JSTaggedValue>> globalSharedConstpools_ {};
274     int32_t sharedConstpoolCount_ = 0; // shared constpool count.
275     std::set<int32_t> freeSharedConstpoolIndex_ {}; // reuse shared constpool index.
276 
277     // Runtime instance and VMs creation.
278     static int32_t vmCount_;
279     static int32_t destroyCount_;
280     static bool firstVmCreated_;
281     static Mutex *vmCreationLock_;
282     static Runtime *instance_;
283 
284     // for string cache
285     JSTaggedValue *externalRegisteredStringTable_ {nullptr};
286     uint32_t registeredStringTableSize_ = 0;
287 
288     friend class EcmaVM;
289     friend class JSThread;
290     friend class SharedHeap;
291 
292     NO_COPY_SEMANTIC(Runtime);
293     NO_MOVE_SEMANTIC(Runtime);
294 };
295 }  // namespace panda::ecmascript
296 #endif // ECMASCRIPT_RUNTIME_H
297