1 /* 2 * Copyright (c) 2022 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_MEM_WORK_MANAGER_H 17 #define ECMASCRIPT_MEM_WORK_MANAGER_H 18 19 #include "ecmascript/mem/mark_stack.h" 20 #include "ecmascript/mem/slots.h" 21 #include "ecmascript/mem/work_space_chunk.h" 22 #include "ecmascript/taskpool/taskpool.h" 23 24 namespace panda::ecmascript { 25 using SlotNeedUpdate = std::pair<TaggedObject *, ObjectSlot>; 26 27 static constexpr uint32_t MARKSTACK_MAX_SIZE = 100; 28 static constexpr uint32_t STACK_AREA_SIZE = sizeof(uintptr_t) * MARKSTACK_MAX_SIZE; 29 30 class Heap; 31 class SharedHeap; 32 class Stack; 33 class SemiSpaceCollector; 34 class TlabAllocator; 35 class SharedTlabAllocator; 36 class Region; 37 class WorkSpaceChunk; 38 39 enum ParallelGCTaskPhase { 40 SEMI_HANDLE_THREAD_ROOTS_TASK, 41 SEMI_HANDLE_SNAPSHOT_TASK, 42 SEMI_HANDLE_GLOBAL_POOL_TASK, 43 OLD_HANDLE_GLOBAL_POOL_TASK, 44 COMPRESS_HANDLE_GLOBAL_POOL_TASK, 45 CONCURRENT_HANDLE_GLOBAL_POOL_TASK, 46 CONCURRENT_HANDLE_OLD_TO_NEW_TASK, 47 UNDEFINED_TASK, 48 TASK_LAST // Count of different Task phase 49 }; 50 51 enum SharedParallelMarkPhase { 52 SHARED_MARK_TASK, 53 SHARED_COMPRESS_TASK, 54 SHARED_UNDEFINED_TASK, 55 SHARED_TASK_LAST // Count of different Task phase 56 }; 57 58 class WorkNode { 59 public: WorkNode(Stack * stack)60 explicit WorkNode(Stack *stack) : next_(nullptr), stack_(stack) {} 61 ~WorkNode() = default; 62 63 NO_COPY_SEMANTIC(WorkNode); 64 NO_MOVE_SEMANTIC(WorkNode); 65 PushObject(uintptr_t obj)66 bool PushObject(uintptr_t obj) 67 { 68 return stack_->PushBackChecked(obj); 69 } 70 PopObject(uintptr_t * obj)71 bool PopObject(uintptr_t *obj) 72 { 73 if (IsEmpty()) { 74 return false; 75 } 76 *obj = stack_->PopBackUnchecked(); 77 return true; 78 } 79 IsEmpty()80 bool IsEmpty() const 81 { 82 return stack_->IsEmpty(); 83 } 84 Next()85 WorkNode *Next() const 86 { 87 return next_; 88 } 89 SetNext(WorkNode * node)90 void SetNext(WorkNode *node) 91 { 92 next_ = node; 93 } 94 95 private: 96 WorkNode *next_; 97 Stack *stack_; 98 }; 99 100 class GlobalWorkStack { 101 public: GlobalWorkStack()102 GlobalWorkStack() : top_(nullptr) {} 103 ~GlobalWorkStack() = default; 104 105 NO_COPY_SEMANTIC(GlobalWorkStack); 106 NO_MOVE_SEMANTIC(GlobalWorkStack); 107 Push(WorkNode * node)108 void Push(WorkNode *node) 109 { 110 if (node == nullptr) { 111 return; 112 } 113 LockHolder lock(mtx_); 114 node->SetNext(top_); 115 top_ = node; 116 } 117 Pop(WorkNode ** node)118 bool Pop(WorkNode **node) 119 { 120 LockHolder lock(mtx_); 121 if (top_ == nullptr) { 122 return false; 123 } 124 *node = top_; 125 top_ = top_->Next(); 126 return true; 127 } 128 private: 129 WorkNode *top_ {nullptr}; 130 Mutex mtx_; 131 }; 132 133 struct WorkNodeHolder { 134 WorkNode *inNode_ {nullptr}; 135 WorkNode *outNode_ {nullptr}; 136 ProcessQueue *weakQueue_ {nullptr}; 137 std::vector<SlotNeedUpdate> pendingUpdateSlots_; 138 TlabAllocator *allocator_ {nullptr}; 139 size_t aliveSize_ = 0; 140 size_t promotedSize_ = 0; 141 }; 142 143 class WorkManagerBase { 144 public: 145 WorkManagerBase(NativeAreaAllocator *allocator); 146 virtual ~WorkManagerBase(); 147 GetSpaceChunk()148 WorkSpaceChunk *GetSpaceChunk() const 149 { 150 return const_cast<WorkSpaceChunk *>(&spaceChunk_); 151 } 152 InitializeBase()153 void InitializeBase() 154 { 155 spaceStart_ = workSpace_; 156 spaceEnd_ = workSpace_ + WORKNODE_SPACE_SIZE; 157 } 158 FinishBase()159 void FinishBase() 160 { 161 while (!agedSpaces_.empty()) { 162 GetSpaceChunk()->Free(reinterpret_cast<void *>(agedSpaces_.back())); 163 agedSpaces_.pop_back(); 164 } 165 } 166 167 WorkNode *AllocateWorkNode(); Finish()168 virtual size_t Finish() 169 { 170 LOG_ECMA(FATAL) << " WorkManagerBase Finish"; 171 return 0; 172 } 173 174 Mutex mtx_; 175 private: 176 NO_COPY_SEMANTIC(WorkManagerBase); 177 NO_MOVE_SEMANTIC(WorkManagerBase); 178 179 WorkSpaceChunk spaceChunk_; 180 uintptr_t workSpace_; 181 uintptr_t spaceStart_; 182 uintptr_t spaceEnd_; 183 std::vector<uintptr_t> agedSpaces_; 184 }; 185 186 class WorkManager : public WorkManagerBase { 187 public: 188 WorkManager() = delete; 189 WorkManager(Heap *heap, uint32_t threadNum); 190 ~WorkManager() override; 191 192 void Initialize(TriggerGCType gcType, ParallelGCTaskPhase taskPhase); 193 size_t Finish() override; 194 void Finish(size_t &aliveSize, size_t &promotedSize); 195 196 bool Push(uint32_t threadId, TaggedObject *object); 197 bool Pop(uint32_t threadId, TaggedObject **object); 198 bool PopWorkNodeFromGlobal(uint32_t threadId); 199 void PushWorkNodeToGlobal(uint32_t threadId, bool postTask = true); 200 PushWeakReference(uint32_t threadId,JSTaggedType * weak)201 inline void PushWeakReference(uint32_t threadId, JSTaggedType *weak) 202 { 203 works_.at(threadId).weakQueue_->PushBack(weak); 204 } 205 IncreaseAliveSize(uint32_t threadId,size_t size)206 inline void IncreaseAliveSize(uint32_t threadId, size_t size) 207 { 208 works_.at(threadId).aliveSize_ += size; 209 } 210 IncreasePromotedSize(uint32_t threadId,size_t size)211 inline void IncreasePromotedSize(uint32_t threadId, size_t size) 212 { 213 works_.at(threadId).promotedSize_ += size; 214 } 215 GetWeakReferenceQueue(uint32_t threadId)216 inline ProcessQueue *GetWeakReferenceQueue(uint32_t threadId) const 217 { 218 return works_.at(threadId).weakQueue_; 219 } 220 GetTlabAllocator(uint32_t threadId)221 inline TlabAllocator *GetTlabAllocator(uint32_t threadId) const 222 { 223 return works_.at(threadId).allocator_; 224 } 225 PushSlotNeedUpdate(uint32_t threadId,SlotNeedUpdate slot)226 inline void PushSlotNeedUpdate(uint32_t threadId, SlotNeedUpdate slot) 227 { 228 works_.at(threadId).pendingUpdateSlots_.emplace_back(slot); 229 } 230 GetSlotNeedUpdate(uint32_t threadId,SlotNeedUpdate * slot)231 inline bool GetSlotNeedUpdate(uint32_t threadId, SlotNeedUpdate *slot) 232 { 233 std::vector<SlotNeedUpdate> &pendingUpdateSlots = works_.at(threadId).pendingUpdateSlots_; 234 if (pendingUpdateSlots.empty()) { 235 return false; 236 } 237 *slot = pendingUpdateSlots.back(); 238 pendingUpdateSlots.pop_back(); 239 return true; 240 } 241 GetTotalThreadNum()242 inline uint32_t GetTotalThreadNum() 243 { 244 return threadNum_; 245 } 246 HasInitialized()247 inline bool HasInitialized() const 248 { 249 return initialized_.load(std::memory_order_acquire); 250 } 251 252 private: 253 NO_COPY_SEMANTIC(WorkManager); 254 NO_MOVE_SEMANTIC(WorkManager); 255 256 Heap *heap_; 257 uint32_t threadNum_; 258 std::array<WorkNodeHolder, MAX_TASKPOOL_THREAD_NUM + 1> works_; 259 std::array<ContinuousStack<JSTaggedType> *, MAX_TASKPOOL_THREAD_NUM + 1> continuousQueue_; 260 GlobalWorkStack workStack_; 261 ParallelGCTaskPhase parallelGCTaskPhase_; 262 std::atomic<bool> initialized_ {false}; 263 }; 264 265 struct SharedGCWorkNodeHolder { 266 WorkNode *inNode_ {nullptr}; 267 WorkNode *outNode_ {nullptr}; 268 ProcessQueue *weakQueue_ {nullptr}; 269 SharedTlabAllocator *allocator_ {nullptr}; 270 size_t aliveSize_ = 0; 271 }; 272 273 class SharedGCWorkManager : public WorkManagerBase { 274 public: 275 SharedGCWorkManager(SharedHeap *heap, uint32_t threadNum); 276 ~SharedGCWorkManager() override; 277 278 void Initialize(TriggerGCType gcType, SharedParallelMarkPhase taskPhase); 279 size_t Finish() override; 280 GetTlabAllocator(uint32_t threadId)281 inline SharedTlabAllocator *GetTlabAllocator(uint32_t threadId) const 282 { 283 return works_.at(threadId).allocator_; 284 } 285 IncreaseAliveSize(uint32_t threadId,size_t size)286 inline void IncreaseAliveSize(uint32_t threadId, size_t size) 287 { 288 works_.at(threadId).aliveSize_ += size; 289 } 290 291 bool Push(uint32_t threadId, TaggedObject *object); 292 bool PushToLocalMarkingBuffer(WorkNode *&markingBuffer, TaggedObject *object); 293 bool Pop(uint32_t threadId, TaggedObject **object); 294 295 bool PopWorkNodeFromGlobal(uint32_t threadId); 296 void PushWorkNodeToGlobal(uint32_t threadId, bool postTask = true); 297 void PushLocalBufferToGlobal(WorkNode *&node, bool postTask = true); 298 PushWeakReference(uint32_t threadId,JSTaggedType * weak)299 inline void PushWeakReference(uint32_t threadId, JSTaggedType *weak) 300 { 301 works_.at(threadId).weakQueue_->PushBack(weak); 302 } 303 GetWeakReferenceQueue(uint32_t threadId)304 inline ProcessQueue *GetWeakReferenceQueue(uint32_t threadId) const 305 { 306 return works_.at(threadId).weakQueue_; 307 } 308 GetTotalThreadNum()309 inline uint32_t GetTotalThreadNum() 310 { 311 return threadNum_; 312 } 313 HasInitialized()314 inline bool HasInitialized() const 315 { 316 return initialized_.load(std::memory_order_acquire); 317 } 318 319 private: 320 NO_COPY_SEMANTIC(SharedGCWorkManager); 321 NO_MOVE_SEMANTIC(SharedGCWorkManager); 322 323 SharedHeap *sHeap_; 324 uint32_t threadNum_; 325 std::array<SharedGCWorkNodeHolder, MAX_TASKPOOL_THREAD_NUM + 1> works_; 326 std::array<ContinuousStack<JSTaggedType> *, MAX_TASKPOOL_THREAD_NUM + 1> continuousQueue_; 327 GlobalWorkStack workStack_; 328 std::atomic<bool> initialized_ {false}; 329 SharedParallelMarkPhase sharedTaskPhase_; 330 }; 331 } // namespace panda::ecmascript 332 #endif // ECMASCRIPT_MEM_WORK_MANAGER_H 333