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