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 24namespace panda::ecmascript { 25using SlotNeedUpdate = std::pair<TaggedObject *, ObjectSlot>; 26 27static constexpr uint32_t MARKSTACK_MAX_SIZE = 100; 28static constexpr uint32_t STACK_AREA_SIZE = sizeof(uintptr_t) * MARKSTACK_MAX_SIZE; 29 30class Heap; 31class SharedHeap; 32class Stack; 33class SemiSpaceCollector; 34class TlabAllocator; 35class SharedTlabAllocator; 36class Region; 37class WorkSpaceChunk; 38 39enum 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 51enum SharedParallelMarkPhase { 52 SHARED_MARK_TASK, 53 SHARED_COMPRESS_TASK, 54 SHARED_UNDEFINED_TASK, 55 SHARED_TASK_LAST // Count of different Task phase 56}; 57 58class WorkNode { 59public: 60 explicit WorkNode(Stack *stack) : next_(nullptr), stack_(stack) {} 61 ~WorkNode() = default; 62 63 NO_COPY_SEMANTIC(WorkNode); 64 NO_MOVE_SEMANTIC(WorkNode); 65 66 bool PushObject(uintptr_t obj) 67 { 68 return stack_->PushBackChecked(obj); 69 } 70 71 bool PopObject(uintptr_t *obj) 72 { 73 if (IsEmpty()) { 74 return false; 75 } 76 *obj = stack_->PopBackUnchecked(); 77 return true; 78 } 79 80 bool IsEmpty() const 81 { 82 return stack_->IsEmpty(); 83 } 84 85 WorkNode *Next() const 86 { 87 return next_; 88 } 89 90 void SetNext(WorkNode *node) 91 { 92 next_ = node; 93 } 94 95private: 96 WorkNode *next_; 97 Stack *stack_; 98}; 99 100class GlobalWorkStack { 101public: 102 GlobalWorkStack() : top_(nullptr) {} 103 ~GlobalWorkStack() = default; 104 105 NO_COPY_SEMANTIC(GlobalWorkStack); 106 NO_MOVE_SEMANTIC(GlobalWorkStack); 107 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 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 } 128private: 129 WorkNode *top_ {nullptr}; 130 Mutex mtx_; 131}; 132 133struct 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 143class WorkManagerBase { 144public: 145 WorkManagerBase(NativeAreaAllocator *allocator); 146 virtual ~WorkManagerBase(); 147 148 WorkSpaceChunk *GetSpaceChunk() const 149 { 150 return const_cast<WorkSpaceChunk *>(&spaceChunk_); 151 } 152 153 void InitializeBase() 154 { 155 spaceStart_ = workSpace_; 156 spaceEnd_ = workSpace_ + WORKNODE_SPACE_SIZE; 157 } 158 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(); 168 virtual size_t Finish() 169 { 170 LOG_ECMA(FATAL) << " WorkManagerBase Finish"; 171 return 0; 172 } 173 174 Mutex mtx_; 175private: 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 186class WorkManager : public WorkManagerBase { 187public: 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 201 inline void PushWeakReference(uint32_t threadId, JSTaggedType *weak) 202 { 203 works_.at(threadId).weakQueue_->PushBack(weak); 204 } 205 206 inline void IncreaseAliveSize(uint32_t threadId, size_t size) 207 { 208 works_.at(threadId).aliveSize_ += size; 209 } 210 211 inline void IncreasePromotedSize(uint32_t threadId, size_t size) 212 { 213 works_.at(threadId).promotedSize_ += size; 214 } 215 216 inline ProcessQueue *GetWeakReferenceQueue(uint32_t threadId) const 217 { 218 return works_.at(threadId).weakQueue_; 219 } 220 221 inline TlabAllocator *GetTlabAllocator(uint32_t threadId) const 222 { 223 return works_.at(threadId).allocator_; 224 } 225 226 inline void PushSlotNeedUpdate(uint32_t threadId, SlotNeedUpdate slot) 227 { 228 works_.at(threadId).pendingUpdateSlots_.emplace_back(slot); 229 } 230 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 242 inline uint32_t GetTotalThreadNum() 243 { 244 return threadNum_; 245 } 246 247 inline bool HasInitialized() const 248 { 249 return initialized_.load(std::memory_order_acquire); 250 } 251 252private: 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 265struct SharedGCWorkNodeHolder { 266 WorkNode *inNode_ {nullptr}; 267 WorkNode *outNode_ {nullptr}; 268 ProcessQueue *weakQueue_ {nullptr}; 269 SharedTlabAllocator *allocator_ {nullptr}; 270 size_t aliveSize_ = 0; 271}; 272 273class SharedGCWorkManager : public WorkManagerBase { 274public: 275 SharedGCWorkManager(SharedHeap *heap, uint32_t threadNum); 276 ~SharedGCWorkManager() override; 277 278 void Initialize(TriggerGCType gcType, SharedParallelMarkPhase taskPhase); 279 size_t Finish() override; 280 281 inline SharedTlabAllocator *GetTlabAllocator(uint32_t threadId) const 282 { 283 return works_.at(threadId).allocator_; 284 } 285 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 299 inline void PushWeakReference(uint32_t threadId, JSTaggedType *weak) 300 { 301 works_.at(threadId).weakQueue_->PushBack(weak); 302 } 303 304 inline ProcessQueue *GetWeakReferenceQueue(uint32_t threadId) const 305 { 306 return works_.at(threadId).weakQueue_; 307 } 308 309 inline uint32_t GetTotalThreadNum() 310 { 311 return threadNum_; 312 } 313 314 inline bool HasInitialized() const 315 { 316 return initialized_.load(std::memory_order_acquire); 317 } 318 319private: 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