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