1 /*
2 * Copyright (c) 2024 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 #include "ark_idle_monitor.h"
17
18
19 #include "utils/log.h"
20 #if defined(ENABLE_FFRT)
21 #include "ffrt.h"
22 #include "c/executor_task.h"
23 #endif
24 #ifdef ENABLE_UCOLLECTION
25 #include "cpu_collector_client.h"
26 #endif
27 #if defined(ENABLE_HITRACE) || defined(ENABLE_EVENT_HANDLER)
28 #include "hitrace/trace.h"
29 #include "hitrace_meter.h"
30 #include "parameter.h"
31 #include "parameters.h"
32 #include "musl_preinit_common.h"
33 #include "memory_trace.h"
34 #endif
35 #include "ark_native_engine.h"
36
37 namespace panda::ecmascript {
38 #if defined(ENABLE_EVENT_HANDLER)
39 static constexpr uint64_t IDLE_GC_TIME_MIN = 1000;
40 static constexpr uint64_t IDLE_GC_TIME_MAX = 10000;
41 uint64_t ArkIdleMonitor::gIdleMonitoringInterval = ArkIdleMonitor::GetIdleMonitoringInterval();
42 bool ArkIdleMonitor::gEnableIdleGC =
43 OHOS::system::GetBoolParameter("persist.ark.enableidlegc", true);
44 #else
45 uint64_t ArkIdleMonitor::gIdleMonitoringInterval = 1000; // ms
46 #endif
47 // gDelayOverTime Detect whether there is any process freezing during the delay process of the delay task
48 uint64_t ArkIdleMonitor::gDelayOverTime = gIdleMonitoringInterval + 100; // ms
49
NotifyLooperIdleStart(int64_t timestamp,int idleTime)50 void ArkIdleMonitor::NotifyLooperIdleStart(int64_t timestamp, [[maybe_unused]] int idleTime)
51 {
52 SetIdleState(true);
53 AddIdleNotifyCount();
54 recordedRunningNotifyInterval_.Push(timestamp - idleEndTimestamp_);
55 #ifndef DISABLE_SHORT_IDLE_CHECK
56 CheckShortIdleTask(timestamp, idleTime);
57 #endif
58 SetNotifyTimestamp(timestamp);
59 }
60
CheckShortIdleTask(int64_t timestamp,int idleTime)61 void ArkIdleMonitor::CheckShortIdleTask(int64_t timestamp, int idleTime)
62 {
63 #if defined(ENABLE_FFRT)
64 while (handlerWaitToStopCount_ > 0) {
65 if (timerHandlerQueue_.size() <= 0) {
66 handlerWaitToStopCount_ = 0;
67 break;
68 }
69 int handler = timerHandlerQueue_.front();
70 timerHandlerQueue_.pop();
71 int ret = ffrt_timer_stop(ffrt_qos_user_initiated, handler);
72 if (ret != 0) {
73 HILOG_ERROR("ArkIdleMonitor: ffrt_timer_stop error handler: timerHandler='%{public}d', ret='%{public}d'",
74 handler, ret);
75 }
76 handlerWaitToStopCount_--;
77 }
78 #endif
79 if (triggeredGC_ && mainVM_ != nullptr) {
80 #ifdef ENABLE_HITRACE
81 StartTrace(HITRACE_TAG_ACE, "NotifyLooperIdleStart::TriggeredGC");
82 #endif
83 triggeredGC_ = JSNApi::NotifyLooperIdleStart(mainVM_, timestamp, idleTime);
84 #ifdef ENABLE_HITRACE
85 FinishTrace(HITRACE_TAG_ACE);
86 #endif
87 return;
88 }
89 if (ShouldTryTriggerGC(timestamp - GetNotifyTimestamp()) &&
90 idleTime > MIN_TRIGGER_GC_IDLE_INTERVAL &&
91 needCheckIntervalIdle_) {
92 PostIdleCheckTask();
93 }
94 if (!needCheckIntervalIdle_) {
95 needCheckIntervalIdle_ = true;
96 }
97 }
98
ShouldTryTriggerGC(int64_t interval)99 bool ArkIdleMonitor::ShouldTryTriggerGC(int64_t interval)
100 {
101 if (interval < MIN_TRIGGER_GC_IDLE_INTERVAL ||
102 recordedIdleNotifyInterval_.Count() != IDLE_CHECK_INTERVAL_LENGTH) {
103 return false;
104 }
105 int64_t sumIdleInterval = recordedIdleNotifyInterval_.Sum([](int64_t a, int64_t b) {return a + b;}, 0);
106 int64_t averageIdleInterval = sumIdleInterval / recordedIdleNotifyInterval_.Count();
107 int64_t sumRunningInterval = recordedRunningNotifyInterval_.Sum([](int64_t a, int64_t b) {return a + b;}, 0);
108 int64_t averageRunningInterval = sumRunningInterval / recordedRunningNotifyInterval_.Count();
109 if (averageIdleInterval > MIN_TRIGGER_GC_IDLE_INTERVAL &&
110 averageRunningInterval <= MAX_TRIGGER_GC_RUNNING_INTERVAL) {
111 return true;
112 }
113 return false;
114 }
115
NotifyLooperIdleEnd(int64_t timestamp)116 void ArkIdleMonitor::NotifyLooperIdleEnd(int64_t timestamp)
117 {
118 idleEndTimestamp_ = timestamp;
119 SetIdleState(false);
120 int64_t duration = timestamp - GetNotifyTimestamp();
121 recordedIdleNotifyInterval_.Push(duration);
122 AddIdleDuration(duration);
123 if (mainVM_ != nullptr) {
124 JSNApi::NotifyLooperIdleEnd(mainVM_, timestamp);
125 }
126 }
127
CheckLowNotifyState() const128 bool ArkIdleMonitor::CheckLowNotifyState() const
129 {
130 uint32_t checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
131 HILOG_DEBUG("ArkIdleMonitor: low Notify checkCounts '%{public}d', result '%{public}d' ",
132 checkCounts, static_cast<int>(numberOfLowIdleNotifyCycles_));
133 return numberOfLowIdleNotifyCycles_ >= checkCounts;
134 }
135
CheckLowRunningDurationState() const136 bool ArkIdleMonitor::CheckLowRunningDurationState() const
137 {
138 uint32_t checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
139 HILOG_DEBUG("ArkIdleMonitor: low Duration checkCounts '%{public}d', result '%{public}d' ",
140 checkCounts, static_cast<int>(numberOfHighIdleTimeRatio_));
141 return numberOfHighIdleTimeRatio_ >= checkCounts;
142 }
143
IntervalMonitor()144 void ArkIdleMonitor::IntervalMonitor()
145 {
146 if (!timerMutex_.try_lock()) {
147 HILOG_INFO("ArkIdleMonitor: IntervalMonitor stop by timerMutex_");
148 return;
149 }
150 auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
151 std::chrono::high_resolution_clock::now()).time_since_epoch().count();
152 int64_t idleDuration = GetTotalIdleDuration() - lastTotalIdleDuration_;
153 if (IsIdleState()) {
154 idleDuration += (nowTimestamp - GetNotifyTimestamp());
155 }
156 lastTotalIdleDuration_ = GetTotalIdleDuration();
157 if (GetIdleNotifyCount() <= LOW_IDLE_NOTIFY_THRESHOLD) {
158 numberOfLowIdleNotifyCycles_++;
159 } else {
160 numberOfLowIdleNotifyCycles_ = 0U;
161 }
162 ResetIdleNotifyCount();
163 int64_t recordTotalDuration = nowTimestamp - startRecordTimestamp_;
164 if (recordTotalDuration <= 0) {
165 numberOfHighIdleTimeRatio_ = 0U;
166 HILOG_ERROR("ArkIdleMonitor: recordTotalDuration <= 0");
167 } else {
168 double idleTimeRatio = static_cast<double>(idleDuration) / recordTotalDuration;
169 HILOG_DEBUG("ArkIdleMonitor: idleTimeRatio '%{public}.2f'", idleTimeRatio);
170 idleTimeRatio >= IDLE_RATIO ? numberOfHighIdleTimeRatio_++ : (numberOfHighIdleTimeRatio_ = 0U);
171 }
172 startRecordTimestamp_ = nowTimestamp;
173 CheckWorkerEnvQueue();
174 uint32_t checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
175 uint32_t workThreadCheckCounts = checkCounts - IDLE_WORKER_TRIGGER_COUNT;
176 int64_t intervalDuration = nowTimestamp - intervalTimestamp_;
177 if (numberOfLowIdleNotifyCycles_ >= checkCounts &&
178 numberOfHighIdleTimeRatio_ >= checkCounts &&
179 intervalDuration < static_cast<int64_t>(gDelayOverTime)) {
180 NotifyMainThreadTryCompressGC();
181 PostMonitorTask(SLEEP_MONITORING_INTERVAL);
182 ClearIdleStats();
183 } else if (numberOfLowIdleNotifyCycles_ >= workThreadCheckCounts &&
184 numberOfHighIdleTimeRatio_ >= workThreadCheckCounts &&
185 intervalDuration < static_cast<int64_t>(gDelayOverTime)) {
186 NotifyOneWorkerThreadTryCompressGC();
187 PostMonitorTask(gIdleMonitoringInterval);
188 } else {
189 PostMonitorTask(gIdleMonitoringInterval);
190 }
191 intervalTimestamp_ = nowTimestamp;
192 timerMutex_.unlock();
193 }
194
PostMonitorTask(uint64_t delayMs)195 void ArkIdleMonitor::PostMonitorTask(uint64_t delayMs)
196 {
197 #if defined(ENABLE_FFRT)
198 auto task = [](void* idleMonitorPtr) {
199 if (idleMonitorPtr != nullptr) {
200 ArkIdleMonitor* arkIdleMonitor = reinterpret_cast<ArkIdleMonitor *>(idleMonitorPtr);
201 arkIdleMonitor->IntervalMonitor();
202 }
203 };
204 if (waitForStopTimerHandler_ != -1) {
205 int ret = ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
206 if (ret != 0) {
207 HILOG_ERROR("ArkIdleMonitor: ffrt_timer_stop error handler: timerHandler='%{public}d', ret='%{public}d'",
208 waitForStopTimerHandler_, ret);
209 }
210 }
211 waitForStopTimerHandler_ = currentTimerHandler_;
212 currentTimerHandler_ = ffrt_timer_start(ffrt_qos_user_initiated, delayMs, this, task, false);
213 #endif
214 }
215
~ArkIdleMonitor()216 ArkIdleMonitor::~ArkIdleMonitor()
217 {
218 #if defined(ENABLE_FFRT)
219 StopIdleMonitorTimerTask();
220 while (timerHandlerQueue_.size() > 0) {
221 ffrt_timer_stop(ffrt_qos_user_initiated, timerHandlerQueue_.front());
222 timerHandlerQueue_.pop();
223 }
224 #endif
225 }
226
ClearIdleStats()227 void ArkIdleMonitor::ClearIdleStats()
228 {
229 ResetIdleNotifyCount();
230 numberOfLowIdleNotifyCycles_ = 0U;
231 numberOfHighIdleTimeRatio_ = 0U;
232 }
233
NotifyMainThreadTryCompressGC()234 void ArkIdleMonitor::NotifyMainThreadTryCompressGC()
235 {
236 #if defined(ENABLE_EVENT_HANDLER)
237 double cpuUsage = GetCpuUsage();
238 if (cpuUsage >= IDLE_CPU_USAGE) {
239 HILOG_INFO("ArkIdleMonitor: Sending a quiet notification is canceled due to high CPU usage: %{public}.2f",
240 cpuUsage);
241 return;
242 }
243 if (mainThreadHandler_ == nullptr) {
244 mainThreadHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(
245 OHOS::AppExecFwk::EventRunner::GetMainEventRunner());
246 };
247 if (mainVM_ == nullptr) {
248 return;
249 }
250 auto task = [this]() {
251 JSNApi::TriggerIdleGC(mainVM_, TRIGGER_IDLE_GC_TYPE::FULL_GC);
252 if (CheckWorkerEnvQueueAllInIdle()) {
253 JSNApi::TriggerIdleGC(mainVM_, TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC);
254 }
255 };
256 mainThreadHandler_->PostTask(task, "ARKTS_IDLE_COMPRESS", 0, OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
257 #endif
258 }
259
NotifyMainThreadTryCompressGCByBackground()260 void ArkIdleMonitor::NotifyMainThreadTryCompressGCByBackground()
261 {
262 #if defined(ENABLE_EVENT_HANDLER)
263 if (mainVM_ == nullptr) {
264 return;
265 }
266 if (mainThreadHandler_ == nullptr) {
267 mainThreadHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(
268 OHOS::AppExecFwk::EventRunner::GetMainEventRunner());
269 };
270 auto task = [this]() {
271 JSNApi::TriggerIdleGC(mainVM_, TRIGGER_IDLE_GC_TYPE::FULL_GC);
272 JSNApi::TriggerIdleGC(mainVM_, TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC);
273 };
274 if (IsIdleState()) {
275 mainThreadHandler_->PostTask(task, "ARKTS_BACKGROUND_COMPRESS", 0,
276 OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
277 }
278 #endif
279 }
280
SetStartTimerCallback()281 void ArkIdleMonitor::SetStartTimerCallback()
282 {
283 JSNApi::SetStartIdleMonitorCallback([this]() {
284 // prevents duplicate invok to avoid deadlocks
285 if (!started_) {
286 this->IntervalMonitor();
287 started_ = true;
288 }
289 });
290 }
291
PostLooperTriggerIdleGCTask()292 void ArkIdleMonitor::PostLooperTriggerIdleGCTask()
293 {
294 #if defined(ENABLE_EVENT_HANDLER)
295 std::shared_ptr<OHOS::AppExecFwk::EventRunner> mainThreadRunner =
296 OHOS::AppExecFwk::EventRunner::GetMainEventRunner();
297 if (mainThreadRunner.get() == nullptr) {
298 HILOG_FATAL("ArkNativeEngine:: the mainEventRunner is nullptr");
299 return;
300 }
301 std::weak_ptr<ArkIdleMonitor> weakArkIdleMonitor = ArkIdleMonitor::GetInstance();
302 auto callback = [weakArkIdleMonitor](OHOS::AppExecFwk::EventRunnerStage stage,
303 const OHOS::AppExecFwk::StageInfo* info) -> int {
304 auto arkIdleMonitor = weakArkIdleMonitor.lock();
305 if (nullptr == arkIdleMonitor) {
306 HILOG_ERROR("ArkIdleMonitor has been destructed.");
307 return 0;
308 }
309 switch (stage) {
310 case OHOS::AppExecFwk::EventRunnerStage::STAGE_BEFORE_WAITING:
311 arkIdleMonitor->NotifyLooperIdleStart(info->timestamp, info->sleepTime);
312 break;
313 case OHOS::AppExecFwk::EventRunnerStage::STAGE_AFTER_WAITING:
314 arkIdleMonitor->NotifyLooperIdleEnd(info->timestamp);
315 break;
316 default:
317 HILOG_ERROR("this branch is unreachable");
318 }
319 return 0;
320 };
321 uint32_t stage = (static_cast<uint32_t>(OHOS::AppExecFwk::EventRunnerStage::STAGE_BEFORE_WAITING) |
322 static_cast<uint32_t>(OHOS::AppExecFwk::EventRunnerStage::STAGE_AFTER_WAITING));
323 mainThreadRunner->GetEventQueue()->AddObserver(OHOS::AppExecFwk::Observer::ARKTS_GC, stage, callback);
324 #endif
325 }
326
EnableIdleGC(NativeEngine * engine)327 void ArkIdleMonitor::EnableIdleGC(NativeEngine *engine)
328 {
329 #if defined(ENABLE_EVENT_HANDLER)
330 auto vm = const_cast<EcmaVM *>(engine->GetEcmaVm());
331 if (gEnableIdleGC && JSNApi::IsJSMainThreadOfEcmaVM(vm)) {
332 SetMainThreadEcmaVM(vm);
333 JSNApi::SetTriggerGCTaskCallback(vm, [engine](TriggerGCData& data) {
334 engine->PostTriggerGCTask(data);
335 });
336 SetStartTimerCallback();
337 PostLooperTriggerIdleGCTask();
338 } else {
339 RegisterWorkerEnv(reinterpret_cast<napi_env>(engine));
340 }
341 #endif
342 }
343
UnregisterEnv(NativeEngine * engine)344 void ArkIdleMonitor::UnregisterEnv(NativeEngine *engine)
345 {
346 #if defined(ENABLE_EVENT_HANDLER)
347 if (!gEnableIdleGC || !JSNApi::IsJSMainThreadOfEcmaVM(engine->GetEcmaVm())) {
348 UnregisterWorkerEnv(reinterpret_cast<napi_env>(engine));
349 }
350 #endif
351 }
352
GetIdleMonitoringInterval()353 uint64_t ArkIdleMonitor::GetIdleMonitoringInterval()
354 {
355 #if defined(ENABLE_EVENT_HANDLER)
356 uint64_t value = OHOS::system::GetIntParameter("const.arkui.idle_monitoring_interval", 1000); // ms
357 if (value < IDLE_GC_TIME_MIN) {
358 value = IDLE_GC_TIME_MIN;
359 }
360 if (value > IDLE_GC_TIME_MAX) {
361 value = IDLE_GC_TIME_MAX;
362 }
363 return value;
364 #else
365 return gIdleMonitoringInterval;
366 #endif
367 }
368
NotifyChangeBackgroundState(bool inBackground)369 void ArkIdleMonitor::NotifyChangeBackgroundState(bool inBackground)
370 {
371 inBackground_.store(inBackground, std::memory_order_relaxed);
372 ClearIdleStats();
373 if (!started_ && inBackground) {
374 HILOG_DEBUG("ArkIdleMonitor change to background but not started idle check");
375 return;
376 }
377 #if defined(ENABLE_FFRT)
378 if (started_ && inBackground) {
379 HILOG_DEBUG("ArkIdleMonitor post check switch background gc task");
380 StopIdleMonitorTimerTask();
381 PostSwitchBackgroundGCTask();
382 }
383 #endif
384 }
385
GetCpuUsage() const386 double ArkIdleMonitor::GetCpuUsage() const
387 {
388 #ifdef ENABLE_UCOLLECTION
389 auto collector = OHOS::HiviewDFX::UCollectClient::CpuCollector::Create();
390 auto collectResult = collector->GetSysCpuUsage();
391 if (collectResult.retCode == OHOS::HiviewDFX::UCollect::UcError::SUCCESS) {
392 HILOG_DEBUG("ArkIdleMonitor cpu usage: %{public}.2f", collectResult.data);
393 return collectResult.data;
394 }
395 HILOG_ERROR("ArkIdleMonitor get cpu usage failed, error code:%{public}d", collectResult.retCode);
396 #endif
397 return 0.0f;
398 }
399
CheckIntervalIdle(int64_t timestamp,int64_t idleDuration)400 bool ArkIdleMonitor::CheckIntervalIdle(int64_t timestamp, int64_t idleDuration)
401 {
402 if (!IsIdleState()) {
403 return false;
404 }
405 int64_t nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
406 std::chrono::high_resolution_clock::now()).time_since_epoch().count();
407 int64_t sumDuration = nowTimestamp - timestamp;
408 int64_t sumIdleDuration = (GetTotalIdleDuration() - idleDuration) + (nowTimestamp - GetNotifyTimestamp());
409 [[maybe_unused]] double idlePercentage = static_cast<double>(sumIdleDuration) / static_cast<double>(sumDuration);
410 #ifdef ENABLE_HITRACE
411 StartTrace(HITRACE_TAG_ACE, "CheckIntervalIdle::sumDuration:" + std::to_string(sumDuration)
412 + "sumIdleDuration:" + std::to_string(sumIdleDuration)
413 + "idlePercentage" + std::to_string(idlePercentage));
414 #endif
415 #if defined(ENABLE_EVENT_HANDLER)
416 if (idlePercentage > SHORT_IDLE_RATIO && mainVM_!= nullptr) {
417 if (mainThreadHandler_ == nullptr) {
418 mainThreadHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(
419 OHOS::AppExecFwk::EventRunner::GetMainEventRunner());
420 };
421 auto task = [this]() {
422 triggeredGC_ = JSNApi::NotifyLooperIdleStart(mainVM_, 0, 0);
423 needCheckIntervalIdle_ = false;
424 handlerWaitToStopCount_++;
425 // If GC is triggered, reset the statistics to avoid triggering monitoring tasks continuously.
426 if (!triggeredGC_) {
427 recordedIdleNotifyInterval_.Reset();
428 }
429 };
430 mainThreadHandler_->PostTask(task, "ARKTS_IDLE_NOTIFY", 0, OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
431 }
432 #endif
433 #ifdef ENABLE_HITRACE
434 FinishTrace(HITRACE_TAG_ACE);
435 #endif
436 return true;
437 }
438
PostIdleCheckTask()439 void ArkIdleMonitor::PostIdleCheckTask()
440 {
441 #ifdef ENABLE_HITRACE
442 StartTrace(HITRACE_TAG_ACE, "NotifyLooperIdleStart::PostIdleCheckTask");
443 #endif
444 #if defined(ENABLE_FFRT)
445 auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
446 std::chrono::high_resolution_clock::now()).time_since_epoch().count();
447 std::tuple<ArkIdleMonitor*, int64_t, int64_t> myTuple = std::make_tuple(this, nowTimestamp, GetTotalIdleDuration());
448 std::tuple<ArkIdleMonitor*, int64_t, int64_t> *data = new std::tuple<ArkIdleMonitor*, int64_t, int64_t>(myTuple);
449 auto task = [](void* data) {
450 std::tuple<ArkIdleMonitor*, int64_t, int64_t>* tuple =
451 reinterpret_cast<std::tuple<ArkIdleMonitor*, int64_t, int64_t>*>(data);
452 if (tuple == nullptr || std::get<0>(*tuple) == nullptr) {
453 return;
454 }
455 std::get<0>(*tuple)->CheckIntervalIdle(std::get<1>(*tuple), std::get<2>(*tuple));
456 delete tuple;
457 };
458 int timerHandler = ffrt_timer_start(ffrt_qos_user_initiated, SHORT_IDLE_DELAY_INTERVAL,
459 reinterpret_cast<void*>(data), task, false);
460 timerHandlerQueue_.push(timerHandler);
461 #endif
462 #ifdef ENABLE_HITRACE
463 FinishTrace(HITRACE_TAG_ACE);
464 #endif
465 }
466
SwitchBackgroundCheckGCTask(int64_t timestamp,int64_t idleDuration)467 void ArkIdleMonitor::SwitchBackgroundCheckGCTask(int64_t timestamp, int64_t idleDuration)
468 {
469 int64_t nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
470 std::chrono::high_resolution_clock::now()).time_since_epoch().count();
471 int64_t sumDuration = nowTimestamp - timestamp;
472 int64_t sumIdleDuration = (GetTotalIdleDuration() - idleDuration) + (nowTimestamp - GetNotifyTimestamp());
473 double idlePercentage = static_cast<double>(sumIdleDuration) / static_cast<double>(sumDuration);
474 double cpuUsage = GetCpuUsage();
475 if (idlePercentage > BACKGROUND_IDLE_RATIO && cpuUsage <= IDLE_BACKGROUND_CPU_USAGE &&
476 sumDuration < static_cast<int64_t>(gDelayOverTime)) {
477 NotifyMainThreadTryCompressGCByBackground();
478 } else {
479 HILOG_INFO("ArkIdleMonitor cancel BGGCTask,idlePer:%{public}.2f;cpuUsage:%{public}.2f;duration:%{public}s",
480 idlePercentage, cpuUsage, std::to_string(sumDuration).c_str());
481 }
482 StopIdleMonitorTimerTaskAndPostSleepTask();
483 ClearIdleStats();
484 }
485
PostSwitchBackgroundGCTask()486 void ArkIdleMonitor::PostSwitchBackgroundGCTask()
487 {
488 #if defined(ENABLE_FFRT)
489 if (switchBackgroundTimerHandler_ != -1) {
490 ffrt_timer_stop(ffrt_qos_user_initiated, switchBackgroundTimerHandler_);
491 }
492 auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
493 std::chrono::high_resolution_clock::now()).time_since_epoch().count();
494 std::tuple<ArkIdleMonitor*, int64_t, int64_t> myTuple = std::make_tuple(this, nowTimestamp, GetTotalIdleDuration());
495 std::tuple<ArkIdleMonitor*, int64_t, int64_t> *data = new std::tuple<ArkIdleMonitor*, int64_t, int64_t>(myTuple);
496 auto task = [](void* data) {
497 std::tuple<ArkIdleMonitor*, int64_t, int64_t>* tuple =
498 reinterpret_cast<std::tuple<ArkIdleMonitor*, int64_t, int64_t>*>(data);
499 if (tuple == nullptr || std::get<0>(*tuple) == nullptr) {
500 return;
501 }
502 std::get<0>(*tuple)->SwitchBackgroundCheckGCTask(std::get<1>(*tuple), std::get<2>(*tuple));
503 delete tuple;
504 };
505 switchBackgroundTimerHandler_ = ffrt_timer_start(ffrt_qos_user_initiated, gIdleMonitoringInterval,
506 reinterpret_cast<void*>(data), task, false);
507 #endif
508 }
509
CheckWorkerEnvQueue()510 void ArkIdleMonitor::CheckWorkerEnvQueue()
511 {
512 std::lock_guard<std::mutex> lock(queueMutex_);
513 for (size_t i = 0; i < workerEnvQueue_.size(); i++) {
514 napi_env env = workerEnvQueue_.front();
515 auto arkNativeEngine = reinterpret_cast<ArkNativeEngine*>(env);
516 workerEnvQueue_.pop();
517 workerEnvQueue_.push(env);
518 arkNativeEngine->GetWorkerThreadState()->CheckIdleState();
519 HILOG_DEBUG("ArkIdleMonitor::CheckWorkerEnvQueue,tid=%{public}d, workerCount=%{public}d",
520 arkNativeEngine->GetSysTid(), arkNativeEngine->GetWorkerThreadState()->GetCheckCount());
521 }
522 }
523
CheckWorkerEnvQueueAllInIdle()524 bool ArkIdleMonitor::CheckWorkerEnvQueueAllInIdle()
525 {
526 std::lock_guard<std::mutex> lock(queueMutex_);
527 for (size_t i = 0; i < workerEnvQueue_.size(); i++) {
528 napi_env env = workerEnvQueue_.front();
529 auto arkNativeEngine = reinterpret_cast<ArkNativeEngine*>(env);
530 if (arkNativeEngine->GetWorkerThreadState()->GetCheckCount() < IDLE_INBACKGROUND_CHECK_LENGTH) {
531 return false;
532 }
533 workerEnvQueue_.pop();
534 workerEnvQueue_.push(env);
535 }
536 return true;
537 }
538
NotifyOneWorkerThreadTryCompressGC()539 void ArkIdleMonitor::NotifyOneWorkerThreadTryCompressGC()
540 {
541 std::lock_guard<std::mutex> lock(queueMutex_);
542 for (size_t i = 0; i < workerEnvQueue_.size(); i++) {
543 napi_env env = workerEnvQueue_.front();
544 workerEnvQueue_.pop();
545 workerEnvQueue_.push(env);
546 auto arkNativeEngine = reinterpret_cast<ArkNativeEngine*>(env);
547 if (arkNativeEngine->GetWorkerThreadState()->GetCheckCount() >= IDLE_INBACKGROUND_CHECK_LENGTH) {
548 std::pair<void*, uint8_t> data(reinterpret_cast<void*>(const_cast<EcmaVM*>(arkNativeEngine->GetEcmaVm())),
549 static_cast<uint8_t>(TRIGGER_IDLE_GC_TYPE::FULL_GC));
550 arkNativeEngine->PostTriggerGCTask(data);
551 return;
552 }
553 }
554 }
555
StopIdleMonitorTimerTask()556 void ArkIdleMonitor::StopIdleMonitorTimerTask()
557 {
558 #if defined(ENABLE_FFRT)
559 std::lock_guard<std::mutex> lock(timerMutex_);
560 HILOG_INFO("StopIdleMonitorTimerTask get timerMutex_");
561 if (currentTimerHandler_ != -1) {
562 ffrt_timer_stop(ffrt_qos_user_initiated, currentTimerHandler_);
563 currentTimerHandler_ = -1;
564 }
565 if (waitForStopTimerHandler_ != -1) {
566 ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
567 waitForStopTimerHandler_ = -1;
568 }
569 #endif
570 }
571
StopIdleMonitorTimerTaskAndPostSleepTask()572 void ArkIdleMonitor::StopIdleMonitorTimerTaskAndPostSleepTask()
573 {
574 #if defined(ENABLE_FFRT)
575 std::lock_guard<std::mutex> lock(timerMutex_);
576 if (currentTimerHandler_ != -1) {
577 ffrt_timer_stop(ffrt_qos_user_initiated, currentTimerHandler_);
578 currentTimerHandler_ = -1;
579 }
580 if (waitForStopTimerHandler_ != -1) {
581 ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
582 waitForStopTimerHandler_ = -1;
583 }
584 PostMonitorTask(SLEEP_MONITORING_INTERVAL);
585 #endif
586 }
587
588
589 std::shared_ptr<ArkIdleMonitor> ArkIdleMonitor::instance_ = std::make_shared<ArkIdleMonitor>();
590
GetInstance()591 std::shared_ptr<ArkIdleMonitor> ArkIdleMonitor::GetInstance()
592 {
593 return instance_;
594 }
595 }
596
597