1 /*
2 * Copyright (c) 2025 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 #include "common_components/heap/collector/collector_resources.h"
16
17 #include <thread>
18
19 #include "common_components/base/sys_call.h"
20 #include "common_components/heap/collector/collector_proxy.h"
21 #include "common_components/common/run_type.h"
22 #include "common_components/common/scoped_object_access.h"
23 #include "common_components/mutator/mutator_manager.h"
24 #ifdef ENABLE_QOS
25 #include "qos.h"
26 #endif
27
28 namespace common {
29
GCMainThreadEntry(void * arg)30 void* CollectorResources::GCMainThreadEntry(void* arg)
31 {
32 #ifdef __APPLE__
33 int ret = pthread_setname_np("OS_GC_Thread");
34 LOGE_IF(UNLIKELY_CC(ret != 0)) << "pthread setname in CollectorResources::StartGCThreads() return " <<
35 ret << " rather than 0";
36 #elif defined(__linux__) || defined(PANDA_TARGET_OHOS)
37 int ret = prctl(PR_SET_NAME, "OS_GC_Thread");
38 LOGE_IF(UNLIKELY_CC(ret != 0)) << "pthread setname in CollectorResources::StartGCThreads() return " <<
39 ret << " rather than 0";
40 #endif
41
42 ASSERT_LOGF(arg != nullptr, "GCMainThreadEntry arg=nullptr");
43 // set current thread as a gc thread.
44 ThreadLocal::SetThreadType(ThreadType::GC_THREAD);
45
46 VLOG(INFO, "CollectorResources Thread begin.");
47
48 #ifdef ENABLE_QOS
49 OHOS::QOS::SetQosForOtherThread(OHOS::QOS::QosLevel::QOS_USER_INITIATED, GetTid());
50 #endif
51
52 // run event loop in this thread.
53 CollectorResources* collectorResources = reinterpret_cast<CollectorResources*>(arg);
54 collectorResources->RunTaskLoop();
55
56 VLOG(INFO, "CollectorResources Thread end.");
57 return nullptr;
58 }
59
Init()60 void CollectorResources::Init()
61 {
62 taskQueue_ = new GCTaskQueue<GCRunner>;
63 taskQueue_->Init();
64 finishedGcIndex_ = 0;
65 StartGCThreads();
66 finalizerProcessor_.Start();
67 gcStats_.Init();
68 hasRelease = false;
69 }
70
Fini()71 void CollectorResources::Fini()
72 {
73 if (hasRelease == false) {
74 StopGCWork();
75 ASSERT_LOGF(!finalizerProcessor_.IsRunning(), "Invalid finalizerProcessor status");
76 ASSERT_LOGF(!gcThreadRunning_.load(std::memory_order_relaxed), "Invalid GC thread status");
77 taskQueue_->Finish();
78 delete taskQueue_;
79 taskQueue_ = nullptr;
80 hasRelease = true;
81 }
82 }
83
StopGCWork()84 void CollectorResources::StopGCWork()
85 {
86 finalizerProcessor_.Stop();
87 TerminateGCTask();
88 StopGCThreads();
89 }
90
StartRuntimeThreads()91 void CollectorResources::StartRuntimeThreads()
92 {
93 // For Postfork.
94 Init();
95 }
96
StopRuntimeThreads()97 void CollectorResources::StopRuntimeThreads()
98 {
99 // For Prefork.
100 Fini();
101 }
102
103 // Send terminate task to gc thread.
TerminateGCTask()104 void CollectorResources::TerminateGCTask()
105 {
106 if (gcThreadRunning_.load(std::memory_order_acquire) == false) {
107 return;
108 }
109
110 GCTaskQueue<GCRunner>::GCTaskFilter filter = [](GCRunner&, GCRunner&) { return false; };
111 GCRunner task(GCTask::GCTaskType::GC_TASK_TERMINATE_GC);
112 (void)taskQueue_->EnqueueSync(task, filter); // enqueue to sync queue
113 }
114
115 // Usually called from main thread, wait for collector thread to exit.
StopGCThreads()116 void CollectorResources::StopGCThreads()
117 {
118 if (gcThreadRunning_.load(std::memory_order_acquire) == false) {
119 LOG_COMMON(FATAL) << "[GC] CollectorResources Thread not begin.";
120 UNREACHABLE();
121 }
122 int ret = ::pthread_join(gcMainThread_, nullptr);
123 LOGE_IF(UNLIKELY_CC(ret != 0)) << "::pthread_join() in StopGCThreads() return " << ret;
124 // wait the thread pool stopped.
125 if (gcThreadPool_ != nullptr) {
126 gcThreadPool_->Destroy(0);
127 gcThreadPool_ = nullptr;
128 }
129 gcThreadRunning_.store(false, std::memory_order_release);
130 }
131
RunTaskLoop()132 void CollectorResources::RunTaskLoop()
133 {
134 gcTid_.store(GetTid(), std::memory_order_release);
135 taskQueue_->DrainTaskQueue(&collectorProxy_);
136 NotifyGCFinished(GCTask::TASK_INDEX_GC_EXIT);
137 }
138
139 // For the ignored gc request, check whether need to wait for current gc finish
PostIgnoredGcRequest(GCReason reason)140 void CollectorResources::PostIgnoredGcRequest(GCReason reason)
141 {
142 GCRequest& request = g_gcRequests[reason];
143 if (request.IsSyncGC() && isGcStarted_.load(std::memory_order_seq_cst)) {
144 ScopedEnterSaferegion safeRegion(false);
145 WaitForGCFinish();
146 }
147 }
148
RequestAsyncGC(GCReason reason,GCType gcType)149 void CollectorResources::RequestAsyncGC(GCReason reason, GCType gcType)
150 {
151 // The gc request must be none blocked
152 ASSERT_LOGF(!g_gcRequests[reason].IsSyncGC(), "trigger from unsafe context must be none blocked");
153 GCRunner gcTask(GCTask::GCTaskType::GC_TASK_INVOKE_GC, reason, gcType);
154 // we use async enqueue because this doesn't have locks, lowering the risk
155 // of timeouts when entering safe region due to thread scheduling
156 taskQueue_->EnqueueAsync(gcTask);
157 }
158
RequestGCAndWait(GCReason reason,GCType gcType)159 void CollectorResources::RequestGCAndWait(GCReason reason, GCType gcType)
160 {
161 // Enter saferegion since current thread may blocked by locks.
162 ScopedEnterSaferegion enterSaferegion(false);
163 GCRunner gcTask(GCTask::GCTaskType::GC_TASK_INVOKE_GC, reason, gcType);
164
165 GCTaskQueue<GCRunner>::GCTaskFilter filter = [](GCRunner& oldTask, GCRunner& newTask) {
166 return oldTask.GetGCReason() == newTask.GetGCReason();
167 };
168
169 GCRequest& request = g_gcRequests[reason];
170 // If this gcTask need not to block, just add to async queue
171 if (!request.IsSyncGC()) {
172 taskQueue_->EnqueueAsync(gcTask);
173 return;
174 }
175
176 // If this gcTask need to block,
177 // add gcTask to syncTaskQueue of gcTaskQueue and wait until this gcTask finished
178 std::unique_lock<std::mutex> lock(gcFinishedCondMutex_);
179 uint64_t curThreadSyncIndex = taskQueue_->EnqueueSync(gcTask, filter);
180 // wait until GC finished
181 std::function<bool()> pred = [this, curThreadSyncIndex] {
182 return ((finishedGcIndex_ >= curThreadSyncIndex) || (finishedGcIndex_ == GCTask::TASK_INDEX_GC_EXIT));
183 };
184 gcFinishedCondVar_.wait(lock, pred);
185 }
186
RequestGC(GCReason reason,bool async,GCType gcType)187 void CollectorResources::RequestGC(GCReason reason, bool async, GCType gcType)
188 {
189 if (!IsGCActive()) {
190 return;
191 }
192
193 GCRequest& request = g_gcRequests[reason];
194 uint64_t curTime = TimeUtil::NanoSeconds();
195 request.SetPrevRequestTime(curTime);
196 if (collectorProxy_.ShouldIgnoreRequest(request)
197 || (reason == GCReason::GC_REASON_NATIVE && IsNativeGCInvoked())) {
198 DLOG(ALLOC, "ignore gc request");
199 PostIgnoredGcRequest(reason);
200 } else if (async) {
201 if (reason == GCReason::GC_REASON_NATIVE) {
202 SetIsNativeGCInvoked(true);
203 }
204 RequestAsyncGC(reason, gcType);
205 } else {
206 RequestGCAndWait(reason, gcType);
207 }
208 }
209
NotifyGCFinished(uint64_t gcIndex)210 void CollectorResources::NotifyGCFinished(uint64_t gcIndex)
211 {
212 std::unique_lock<std::mutex> lock(gcFinishedCondMutex_);
213 isGcStarted_.store(false, std::memory_order_relaxed);
214 if (gcIndex >= GCTask::TASK_INDEX_SYNC_GC_MIN) { // sync gc, need set taskIndex
215 finishedGcIndex_.store(gcIndex);
216 }
217 gcFinishedCondVar_.notify_all();
218 BroadcastGCFinished();
219 }
220
MarkGCStart()221 void CollectorResources::MarkGCStart()
222 {
223 std::unique_lock<std::mutex> lock(gcFinishedCondMutex_);
224
225 // Wait for any existing GC to finish - inline the wait logic
226 std::function<bool()> pred = [this] {
227 return !IsGcStarted();
228 };
229 gcFinishedCondVar_.wait(lock, pred);
230
231 // Now claim GC ownership
232 SetGcStarted(true);
233 }
234
MarkGCFinish(uint64_t gcIndex)235 void CollectorResources::MarkGCFinish(uint64_t gcIndex)
236 {
237 NotifyGCFinished(gcIndex);
238 }
239
WaitForGCFinish()240 void CollectorResources::WaitForGCFinish()
241 {
242 uint64_t startTime = TimeUtil::MicroSeconds();
243 std::unique_lock<std::mutex> lock(gcFinishedCondMutex_);
244 uint64_t curWaitGcIndex = finishedGcIndex_.load();
245 std::function<bool()> pred = [this, curWaitGcIndex] {
246 return (!IsGcStarted() || (curWaitGcIndex != finishedGcIndex_) ||
247 (finishedGcIndex_ == GCTask::TASK_INDEX_GC_EXIT));
248 };
249 gcFinishedCondVar_.wait(lock, pred);
250 uint64_t stopTime = TimeUtil::MicroSeconds();
251 uint64_t diffTime = stopTime - startTime;
252 VLOG(DEBUG, "WaitForGCFinish cost %zu us", diffTime);
253 }
254
StartGCThreads()255 void CollectorResources::StartGCThreads()
256 {
257 bool expected = false;
258 if (gcThreadRunning_.compare_exchange_strong(expected, true, std::memory_order_acquire) == false) {
259 LOG_COMMON(FATAL) << "[GC] CollectorResources Thread already begin.";
260 UNREACHABLE();
261 }
262 DCHECK_CC(gcThreadPool_ == nullptr);
263 gcThreadPool_ = Taskpool::GetCurrentTaskpool();
264 gcThreadPool_->Initialize();
265 LOGF_CHECK(gcThreadPool_ != nullptr) << "new GCThreadPool failed";
266 uint32_t helperThreads = gcThreadPool_->GetTotalThreadNum();
267 if (helperThreads > 0) {
268 --helperThreads; // gc task is exclusive, so keep one thread left
269 }
270 // 1 is for gc main thread.
271 gcThreadCount_ = helperThreads + 1;
272 VLOG(DEBUG, "total gc thread count %d, helper thread count %d", gcThreadCount_, helperThreads);
273
274 // create the collector thread.
275 if (::pthread_create(&gcMainThread_, nullptr, CollectorResources::GCMainThreadEntry, this) != 0) {
276 ASSERT_LOGF(0, "pthread_create failed!");
277 }
278 // set thread name.
279 #ifdef __WIN64
280 int ret = pthread_setname_np(gcMainThread_, "OS_GC_Thread");
281 LOGE_IF(UNLIKELY_CC(ret != 0)) << "pthread_setname_np() in CollectorResources::StartGCThreads() return " <<
282 ret << " rather than 0";
283 #endif
284 }
285
GetGCThreadCount(const bool isConcurrent) const286 uint32_t CollectorResources::GetGCThreadCount(const bool isConcurrent) const
287 {
288 if (GetThreadPool() == nullptr) {
289 return 1;
290 } else if (isConcurrent) {
291 return gcThreadCount_;
292 }
293 // default to 2
294 return 2;
295 }
296
BroadcastGCFinished()297 void CollectorResources::BroadcastGCFinished()
298 {
299 gcWorking_ = 0;
300 #if defined(_WIN64) || defined(__APPLE__)
301 WakeWhenGCDone();
302 #else
303 (void)Futex(&gcWorking_, FUTEX_WAKE_PRIVATE, INT_MAX);
304 #endif
305 }
306
RequestHeapDump(GCTask::GCTaskType gcTask)307 void CollectorResources::RequestHeapDump(GCTask::GCTaskType gcTask)
308 {
309 GCTaskQueue<GCRunner>::GCTaskFilter filter = [](GCRunner&, GCRunner&) { return false; };
310 GCRunner dumpTask = GCRunner(gcTask);
311 taskQueue_->EnqueueSync(dumpTask, filter);
312 }
313
314 } // namespace common
315