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