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