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