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 #include <chrono>
19
20 #include "utils/log.h"
21 #if defined(ENABLE_FFRT)
22 #include "ffrt.h"
23 #include "c/executor_task.h"
24 #endif
25
26 namespace panda::ecmascript {
27
NotifyLooperIdleStart(int64_t timestamp, int idleTime)28 void ArkIdleMonitor::NotifyLooperIdleStart(int64_t timestamp, int idleTime)
29 {
30 SetIdleState(true);
31 AddIdleNotifyCount();
32 if (idleTime < MIN_TRIGGER_GC_IDLE_INTERVAL || !ShouldTryTriggerGC(timestamp)) {
33 return;
34 }
35 JSNApi::NotifyLooperIdleStart(vm_, timestamp, idleTime);
36 }
37
ShouldTryTriggerGC(int64_t timestamp)38 bool ArkIdleMonitor::ShouldTryTriggerGC(int64_t timestamp)
39 {
40 int64_t notifyInterval = timestamp - GetNotifyTimestamp();
41 recordedIdleNotifyInterval_.Push(notifyInterval);
42 SetNotifyTimestamp(timestamp);
43 if (notifyInterval < MIN_TRIGGER_GC_IDLE_INTERVAL ||
44 recordedIdleNotifyInterval_.Count() != IDLE_CHECK_INTERVAL_LENGTH) {
45 return false;
46 }
47 int64_t sumInterval = recordedIdleNotifyInterval_.Sum([](int64_t a, int64_t b) {return a + b;}, 0);
48 int64_t averageInterval = sumInterval / recordedIdleNotifyInterval_.Count();
49 if (averageInterval > MIN_TRIGGER_GC_IDLE_INTERVAL && notifyInterval < averageInterval * DOUBLE_INTERVAL_CHECK) {
50 return true;
51 }
52 return false;
53 }
54
NotifyLooperIdleEnd(int64_t timestamp)55 void ArkIdleMonitor::NotifyLooperIdleEnd(int64_t timestamp)
56 {
57 SetIdleState(false);
58 AddIdleDuration(timestamp - GetNotifyTimestamp());
59 JSNApi::NotifyLooperIdleEnd(vm_, timestamp);
60 }
61
CheckLowNotifyState() const62 bool ArkIdleMonitor::CheckLowNotifyState() const
63 {
64 int checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
65 HILOG_DEBUG("ArkIdleMonitor: low Notify checkCounts '%{public}d', result '%{public}d' ",
66 checkCounts, static_cast<int>(numberOfLowIdleNotifyCycles_));
67 return numberOfLowIdleNotifyCycles_ >= static_cast<int64_t>(checkCounts);
68 }
69
CheckLowRunningDurationState() const70 bool ArkIdleMonitor::CheckLowRunningDurationState() const
71 {
72 int checkCounts = IsInBackground() ? IDLE_INBACKGROUND_CHECK_LENGTH : IDLE_CHECK_LENGTH;
73 HILOG_DEBUG("ArkIdleMonitor: low Duration checkCounts '%{public}d', result '%{public}d' ",
74 checkCounts, static_cast<int>(numberOfHighIdleTimeRatio_));
75 return numberOfHighIdleTimeRatio_ >= static_cast<int64_t>(checkCounts);
76 }
77
IntervalMonitor()78 void ArkIdleMonitor::IntervalMonitor()
79 {
80 auto nowTimestamp = std::chrono::time_point_cast<std::chrono::milliseconds>(
81 std::chrono::high_resolution_clock::now()).time_since_epoch().count();
82 if (IsIdleState()) {
83 AddIdleDuration(nowTimestamp - GetNotifyTimestamp());
84 SetNotifyTimestamp(nowTimestamp);
85 }
86 if (GetIdleNotifyCount() <= LOW_IDLE_NOTIFY_THRESHOLD) {
87 numberOfLowIdleNotifyCycles_++;
88 } else {
89 numberOfLowIdleNotifyCycles_ = 0;
90 }
91 ResetIdleNotifyCount();
92 int64_t recordTotalDuration = nowTimestamp - startRecordTimestamp_;
93 if (recordTotalDuration <= 0) {
94 numberOfHighIdleTimeRatio_ = 0;
95 HILOG_ERROR("ArkIdleMonitor: recordTotalDuration <= 0");
96 } else {
97 double idleTimeRatio = static_cast<double>(GetTotalIdleDuration()) / recordTotalDuration;
98 idleTimeRatio >= IDLE_RATIO ? numberOfHighIdleTimeRatio_++ : (numberOfHighIdleTimeRatio_ = 0);
99 }
100 startRecordTimestamp_ = nowTimestamp;
101 ResetTotalIdleDuration();
102
103 if (CheckLowNotifyState() && CheckLowRunningDurationState()) {
104 NotifyTryCompressGC();
105 PostMonitorTask(SLEEP_MONITORING_INTERVAL);
106 ClearIdleStats();
107 } else {
108 PostMonitorTask(IDLE_MONITORING_INTERVAL);
109 }
110 }
111
PostMonitorTask(uint64_t delayMs)112 void ArkIdleMonitor::PostMonitorTask(uint64_t delayMs)
113 {
114 #if defined(ENABLE_FFRT)
115 auto task = [](void* idleMonitorPtr) {
116 if (idleMonitorPtr != nullptr) {
117 ArkIdleMonitor* arkIdleMonitor = reinterpret_cast<ArkIdleMonitor *>(idleMonitorPtr);
118 arkIdleMonitor->IntervalMonitor();
119 }
120 };
121 if (waitForStopTimerHandler_ != -1) {
122 int ret = ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
123 if (ret != 0) {
124 HILOG_ERROR("ArkIdleMonitor: ffrt_timer_stop error handler: timerHandler='%{public}d', ret='%{public}d'",
125 waitForStopTimerHandler_, ret);
126 }
127 }
128 waitForStopTimerHandler_ = currentTimerHandler_;
129 currentTimerHandler_ = ffrt_timer_start(ffrt_qos_user_initiated, delayMs, this, task, false);
130 #endif
131 }
132
~ArkIdleMonitor()133 ArkIdleMonitor::~ArkIdleMonitor()
134 {
135 #if defined(ENABLE_FFRT)
136 if (waitForStopTimerHandler_ != -1) {
137 ffrt_timer_stop(ffrt_qos_user_initiated, waitForStopTimerHandler_);
138 }
139 if (currentTimerHandler_ != -1) {
140 ffrt_timer_stop(ffrt_qos_user_initiated, currentTimerHandler_);
141 }
142 #endif
143 }
144
ClearIdleStats()145 void ArkIdleMonitor::ClearIdleStats()
146 {
147 ResetIdleNotifyCount();
148 ResetTotalIdleDuration();
149 startRecordTimestamp_ = 0;
150 numberOfLowIdleNotifyCycles_ = 0;
151 numberOfHighIdleTimeRatio_ = 0;
152 }
153
NotifyTryCompressGC()154 void ArkIdleMonitor::NotifyTryCompressGC()
155 {
156 #if defined(ENABLE_EVENT_HANDLER)
157 if (mainThreadHandler_ == nullptr) {
158 mainThreadHandler_ = std::make_shared<OHOS::AppExecFwk::EventHandler>(
159 OHOS::AppExecFwk::EventRunner::GetMainEventRunner());
160 };
161 auto task = [this]() {
162 JSNApi::TriggerIdleGC(vm_, TRIGGER_IDLE_GC_TYPE::FULL_GC);
163 JSNApi::TriggerIdleGC(vm_, TRIGGER_IDLE_GC_TYPE::SHARED_FULL_GC);
164 };
165 mainThreadHandler_->PostTask(task, "ARKTS_IDLE_COMPRESS", 0, OHOS::AppExecFwk::EventQueue::Priority::IMMEDIATE);
166 #endif
167 }
168
SetStartTimerCallback()169 void ArkIdleMonitor::SetStartTimerCallback()
170 {
171 JSNApi::SetStartIdleMonitorCallback([this]() {
172 this->PostMonitorTask();
173 });
174 }
175
NotifyChangeBackgroundState(bool inBackground)176 void ArkIdleMonitor::NotifyChangeBackgroundState(bool inBackground)
177 {
178 inBackground_.store(inBackground, std::memory_order_relaxed);
179 ClearIdleStats();
180 }
181 }
182
183