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