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