• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-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 PANDA_RUNTIME_MEM_GC_GC_WORKERS_THREAD_POOL_H
17 #define PANDA_RUNTIME_MEM_GC_GC_WORKERS_THREAD_POOL_H
18 
19 #include "runtime/include/thread.h"
20 #include "runtime/thread_pool.h"
21 #include "runtime/thread_pool_queue.h"
22 #include "runtime/mem/gc/gc.h"
23 
24 namespace panda::mem {
25 class GCWorkersThreadPool;
26 class Region;
27 
GCWorkersTaskTypesToString(GCWorkersTaskTypes type)28 inline static const char *GCWorkersTaskTypesToString(GCWorkersTaskTypes type)
29 {
30     switch (type) {
31         case GCWorkersTaskTypes::TASK_EMPTY:
32             return "Empty task";
33         case GCWorkersTaskTypes::TASK_MARKING:
34             return "Marking task";
35         case GCWorkersTaskTypes::TASK_REMARK:
36             return "Remark task";
37         case GCWorkersTaskTypes::TASK_REGION_COMPACTING:
38             return "Region compacting task";
39         case GCWorkersTaskTypes::TASK_ROOT_COLLECTION:
40             return "Root collection task";
41         case GCWorkersTaskTypes::TASK_MOVE_YOUNG_ALIVE_OBJECTS:
42             return "Move alive young objects task";
43         case GCWorkersTaskTypes::TASK_INIT_REFS_FROM_REMSETS:
44             return "Initialize references from remsets task";
45         default:
46             return "Unknown task";
47     }
48 }
49 
50 class RefInfo {
51     static constexpr uint32_t VOLATILE_MASK = 1U;
52 
53 public:
54     RefInfo() = default;
55 
RefInfo(ObjectHeader * object,uint32_t ref_offset,bool is_volatile)56     RefInfo(ObjectHeader *object, uint32_t ref_offset, bool is_volatile)
57         : object_(object), ref_offset_(ref_offset | static_cast<uint32_t>(is_volatile))
58     {
59     }
60 
61     ~RefInfo() = default;
62 
GetObject()63     ObjectHeader *GetObject() const
64     {
65         return object_;
66     }
67 
GetReferenceOffset()68     uint32_t GetReferenceOffset() const
69     {
70         return ref_offset_ & ~VOLATILE_MASK;
71     }
72 
IsVolatile()73     bool IsVolatile() const
74     {
75         return (ref_offset_ & VOLATILE_MASK) != 0;
76     }
77 
78     DEFAULT_COPY_SEMANTIC(RefInfo);
79     DEFAULT_MOVE_SEMANTIC(RefInfo);
80 
81 private:
82     ObjectHeader *object_;
83     uint32_t ref_offset_;
84 };
85 
86 class GCWorkersTask : public TaskInterface {
87 public:
88     using StackType = GCMarkingStackType;
89     using RegionDataType = std::pair<PandaVector<ObjectHeader *> *, Region *>;
90     using RefVector = PandaVector<RefInfo>;
91 
GCWorkersTask()92     GCWorkersTask()
93 
94     {
95         task_type_ = GCWorkersTaskTypes::TASK_EMPTY;
96     }
97 
GCWorkersTask(GCWorkersTaskTypes type,StackType * marking_stack)98     explicit GCWorkersTask(GCWorkersTaskTypes type, StackType *marking_stack) : task_type_(type)
99     {
100         ASSERT(type == GCWorkersTaskTypes::TASK_MARKING || type == GCWorkersTaskTypes::TASK_REMARK);
101         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
102         storage_.marking_stack = marking_stack;
103     }
104 
GCWorkersTask(GCWorkersTaskTypes type,RegionDataType * region_data)105     explicit GCWorkersTask(GCWorkersTaskTypes type, RegionDataType *region_data) : task_type_(type)
106     {
107         ASSERT(type == GCWorkersTaskTypes::TASK_REGION_COMPACTING);
108         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
109         storage_.region_data = region_data;
110     }
111 
GCWorkersTask(GCWorkersTaskTypes type,CardTable::CardPtr card)112     explicit GCWorkersTask(GCWorkersTaskTypes type, CardTable::CardPtr card) : task_type_(type)
113     {
114         ASSERT(type == GCWorkersTaskTypes::TASK_INIT_REFS_FROM_REMSETS);
115         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
116         storage_.card = card;
117     }
118 
119     ~GCWorkersTask() = default;
120     DEFAULT_COPY_SEMANTIC(GCWorkersTask);
121     // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
122     DEFAULT_MOVE_SEMANTIC(GCWorkersTask);
123 
IsEmpty()124     bool IsEmpty() const
125     {
126         return task_type_ == GCWorkersTaskTypes::TASK_EMPTY;
127     }
128 
GetMarkingStack()129     StackType *GetMarkingStack() const
130     {
131         ASSERT(task_type_ == GCWorkersTaskTypes::TASK_MARKING || task_type_ == GCWorkersTaskTypes::TASK_REMARK);
132         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
133         return storage_.marking_stack;
134     }
135 
GetRegionData()136     RegionDataType *GetRegionData() const
137     {
138         ASSERT(task_type_ == GCWorkersTaskTypes::TASK_REGION_COMPACTING);
139         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
140         return storage_.region_data;
141     }
142 
GetCard()143     CardTable::CardPtr GetCard() const
144     {
145         ASSERT(task_type_ == GCWorkersTaskTypes::TASK_INIT_REFS_FROM_REMSETS);
146         // NOLINTNEXTLINE(cppcoreguidelines-pro-type-union-access)
147         return storage_.card;
148     }
149 
GetType()150     GCWorkersTaskTypes GetType() const
151     {
152         return task_type_;
153     }
154 
155 private:
156     union StorageType {
157         StackType *marking_stack;
158         RegionDataType *region_data;
159         CardTable::CardPtr card;
160     };
161     GCWorkersTaskTypes task_type_;
162     StorageType storage_ {nullptr};
163 };
164 
165 class GCWorkersProcessor : public ProcessorInterface<GCWorkersTask, GCWorkersThreadPool *> {
166 public:
GCWorkersProcessor(GCWorkersThreadPool * gc_threads_pools)167     explicit GCWorkersProcessor(GCWorkersThreadPool *gc_threads_pools)
168         : gc_threads_pools_(gc_threads_pools), worker_data_(nullptr)
169     {
170     }
171 
172     ~GCWorkersProcessor() override = default;
173     NO_COPY_SEMANTIC(GCWorkersProcessor);
174     NO_MOVE_SEMANTIC(GCWorkersProcessor);
175 
176     bool Process(GCWorkersTask task) override;
177     bool Init() override;
178     bool Destroy() override;
179 
180 private:
181     GCWorkersThreadPool *gc_threads_pools_;
182     void *worker_data_;
183 };
184 
185 class GCWorkersQueueSimple : public TaskQueueInterface<GCWorkersTask> {
186 public:
GCWorkersQueueSimple(mem::InternalAllocatorPtr allocator,size_t queue_limit)187     explicit GCWorkersQueueSimple(mem::InternalAllocatorPtr allocator, size_t queue_limit)
188         : TaskQueueInterface<GCWorkersTask>(queue_limit), queue_(allocator->Adapter())
189     {
190     }
191 
192     ~GCWorkersQueueSimple() override = default;
193     NO_COPY_SEMANTIC(GCWorkersQueueSimple);
194     NO_MOVE_SEMANTIC(GCWorkersQueueSimple);
195 
GetTask()196     GCWorkersTask GetTask() override
197     {
198         if (queue_.empty()) {
199             LOG(DEBUG, GC) << "Empty " << queue_name_ << ", return nothing";
200             return GCWorkersTask();
201         }
202         auto task = queue_.front();
203         queue_.pop_front();
204         LOG(DEBUG, GC) << "Extract a task from a " << queue_name_ << ": " << GetTaskDescription(task);
205         return task;
206     }
207 
208     // NOLINTNEXTLINE(google-default-arguments)
209     void AddTask(GCWorkersTask ctx, [[maybe_unused]] size_t priority = 0) override
210     {
211         LOG(DEBUG, GC) << "Add task to a " << queue_name_ << ": " << GetTaskDescription(ctx);
212         queue_.push_front(ctx);
213     }
214 
Finalize()215     void Finalize() override
216     {
217         // Nothing to deallocate
218         LOG(DEBUG, GC) << "Clear a " << queue_name_;
219         queue_.clear();
220     }
221 
222 protected:
GetTaskDescription(const GCWorkersTask & ctx)223     PandaString GetTaskDescription(const GCWorkersTask &ctx)
224     {
225         PandaOStringStream stream;
226         stream << GCWorkersTaskTypesToString(ctx.GetType());
227         return stream.str();
228     }
229 
GetQueueSize()230     size_t GetQueueSize() override
231     {
232         return queue_.size();
233     }
234 
235 private:
236     PandaList<GCWorkersTask> queue_;
237     const char *queue_name_ = "simple gc workers task queue";
238 };
239 
240 class GCWorkersCreationInterface : public WorkerCreationInterface {
241 public:
GCWorkersCreationInterface(PandaVM * vm)242     explicit GCWorkersCreationInterface(PandaVM *vm) : gc_thread_(vm, Thread::ThreadType::THREAD_TYPE_GC)
243     {
244         ASSERT(vm != nullptr);
245     }
246 
247     ~GCWorkersCreationInterface() override = default;
248     NO_COPY_SEMANTIC(GCWorkersCreationInterface);
249     NO_MOVE_SEMANTIC(GCWorkersCreationInterface);
250 
AttachWorker(bool helper_thread)251     void AttachWorker(bool helper_thread) override
252     {
253         if (!helper_thread) {
254             Thread::SetCurrent(&gc_thread_);
255         }
256     }
DetachWorker(bool helper_thread)257     void DetachWorker(bool helper_thread) override
258     {
259         if (!helper_thread) {
260             Thread::SetCurrent(nullptr);
261         }
262     }
263 
264 private:
265     Thread gc_thread_;
266 };
267 
268 class GCWorkersThreadPool {
269 public:
270     NO_COPY_SEMANTIC(GCWorkersThreadPool);
271     NO_MOVE_SEMANTIC(GCWorkersThreadPool);
272     GCWorkersThreadPool(mem::InternalAllocatorPtr internal_allocator, GC *gc, size_t threads_count = 0);
273     ~GCWorkersThreadPool();
274 
275     template <class T>
AddTask(GCWorkersTaskTypes type,T * storage_ptr)276     bool AddTask(GCWorkersTaskTypes type, T *storage_ptr)
277     {
278         IncreaseSendedTasks();
279         GCWorkersTask task {type, storage_ptr};
280         if (thread_pool_->TryPutTask(task)) {
281             LOG(DEBUG, GC) << "Added a new " << GCWorkersTaskTypesToString(type) << " to queue";
282             return true;
283         }
284         DecreaseSendedTasks();
285         return false;
286     }
287 
288     void WaitUntilTasksEnd();
289 
GetGC()290     GC *GetGC()
291     {
292         return gc_;
293     }
294 
IncreaseSolvedTasks()295     void IncreaseSolvedTasks()
296     {
297         solved_tasks_++;
298         if (solved_tasks_ == sended_tasks_) {
299             os::memory::LockHolder lock(cond_var_lock_);
300             cond_var_.Signal();
301         }
302     }
303 
IncreaseSendedTasks()304     void IncreaseSendedTasks()
305     {
306         sended_tasks_++;
307     }
308 
309 private:
310     static constexpr uint64_t ALL_TASK_FINISH_WAIT_TIMEOUT = 100U;
311 
ResetTasks()312     void ResetTasks()
313     {
314         solved_tasks_ = 0;
315         sended_tasks_ = 0;
316     }
317 
GetSolvedTasks()318     size_t GetSolvedTasks()
319     {
320         return solved_tasks_;
321     }
322 
DecreaseSendedTasks()323     void DecreaseSendedTasks()
324     {
325         sended_tasks_--;
326     }
327 
GetSendedTasks()328     size_t GetSendedTasks()
329     {
330         return sended_tasks_;
331     }
332 
333     GC *gc_;
334     ThreadPool<GCWorkersTask, GCWorkersProcessor, GCWorkersThreadPool *> *thread_pool_;
335     GCWorkersQueueSimple *queue_;
336     GCWorkersCreationInterface *worker_iface_;
337     mem::InternalAllocatorPtr internal_allocator_;
338     std::atomic_size_t solved_tasks_ {0};
339     std::atomic_size_t sended_tasks_ {0};
340     os::memory::Mutex cond_var_lock_;
341     os::memory::ConditionVariable cond_var_;
342 };
343 
344 }  // namespace panda::mem
345 
346 #endif  // PANDA_RUNTIME_MEM_GC_GC_WORKERS_THREAD_POOL_H
347