• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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