• 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 #ifndef FOUNDATION_ACE_NAPI_NATIVE_ENGINE_IMPL_ARK_ARK_NATIVE_IDLE_MONITOR_H
17 #define FOUNDATION_ACE_NAPI_NATIVE_ENGINE_IMPL_ARK_ARK_NATIVE_IDLE_MONITOR_H
18 
19 #include <atomic>
20 #include <ctime>
21 #include <chrono>
22 #include <array>
23 #include <algorithm>
24 #include <memory>
25 #include <mutex>
26 #include <queue>
27 
28 #if defined(ENABLE_EVENT_HANDLER)
29 #include "event_handler.h"
30 #endif
31 #include "uv.h"
32 #include "ecmascript/napi/include/dfx_jsnapi.h"
33 #include "ecmascript/napi/include/jsnapi.h"
34 #include "interfaces/inner_api/napi/native_node_api.h"
35 
36 class NativeEngine;
37 
38 namespace panda::ecmascript {
39 class Heap;
40 class SharedHeap;
41 class ConcurrentMarker;
42 class MemController;
43 class SharedMemController;
44 
45 class ArkIdleMonitor {
46 using Clock = std::chrono::high_resolution_clock;
47 using TRIGGER_IDLE_GC_TYPE = panda::JSNApi::TRIGGER_IDLE_GC_TYPE;
48 public:
ArkIdleMonitor()49     ArkIdleMonitor(){};
50     ~ArkIdleMonitor();
51 
52     static std::shared_ptr<ArkIdleMonitor> GetInstance();
53 
IsIdleState()54     bool IsIdleState() const
55     {
56         return idleState_.load(std::memory_order_relaxed);
57     }
58 
SetIdleState(bool idleState)59     void SetIdleState(bool idleState)
60     {
61         idleState_.store(idleState, std::memory_order_relaxed);
62     }
63 
64     void NotifyChangeBackgroundState(bool inBackground);
65 
IsInBackground()66     bool IsInBackground() const
67     {
68         return inBackground_.load(std::memory_order_relaxed);
69     }
70 
AddIdleNotifyCount()71     void AddIdleNotifyCount()
72     {
73         idleNotifyCount_.fetch_add(1, std::memory_order_relaxed);
74     }
75 
GetIdleNotifyCount()76     int64_t GetIdleNotifyCount() const
77     {
78         return idleNotifyCount_.load(std::memory_order_relaxed);
79     }
80 
ResetIdleNotifyCount()81     void ResetIdleNotifyCount()
82     {
83         return idleNotifyCount_.store(0, std::memory_order_relaxed);
84     }
85 
GetNotifyTimestamp()86     int64_t GetNotifyTimestamp() const
87     {
88         return idleStartTimestamp_.load(std::memory_order_relaxed);
89     }
90 
SetNotifyTimestamp(int64_t timestamp)91     void SetNotifyTimestamp(int64_t timestamp)
92     {
93         idleStartTimestamp_.store(timestamp, std::memory_order_relaxed);
94     }
95 
GetTotalIdleDuration()96     int64_t GetTotalIdleDuration() const
97     {
98         return totalIdleDuration_.load(std::memory_order_relaxed);
99     }
100 
ResetTotalIdleDuration()101     void ResetTotalIdleDuration()
102     {
103         totalIdleDuration_.store(0, std::memory_order_relaxed);
104     }
105 
AddIdleDuration(int64_t duration)106     void AddIdleDuration(int64_t duration)
107     {
108         totalIdleDuration_.fetch_add(duration, std::memory_order_relaxed);
109     }
110 
SetMainThreadEcmaVM(EcmaVM * vm)111     void SetMainThreadEcmaVM(EcmaVM* vm)
112     {
113         mainVM_ = vm;
114     }
115 
RegisterWorkerEnv(napi_env workerEnv)116     void RegisterWorkerEnv(napi_env workerEnv)
117     {
118         std::lock_guard<std::mutex> lock(queueMutex_);
119         workerEnvQueue_.push(workerEnv);
120     }
121 
UnregisterWorkerEnv(napi_env workerEnv)122     void UnregisterWorkerEnv(napi_env workerEnv)
123     {
124         std::lock_guard<std::mutex> lock(queueMutex_);
125         for (size_t i = 0; i < workerEnvQueue_.size(); i++) {
126             napi_env env = workerEnvQueue_.front();
127             workerEnvQueue_.pop();
128             if (env == workerEnv) {
129                 return;
130             }
131             workerEnvQueue_.push(env);
132         }
133     }
134 
135     template<typename T, int N>
136     class RingBuffer {
137     public:
138         RingBuffer() = default;
139         ~RingBuffer() = default;
140 
Push(const T & value)141         void Push(const T &value)
142         {
143             if (count_ == N) {
144                 elements_[start_++] = value;
145                 if (start_ == N) {
146                     start_ = 0;
147                 }
148             } else {
149                 ASSERT(start_ == 0);
150                 elements_[count_++] = value;
151             }
152         }
153 
Count()154         int Count() const
155         {
156             return count_;
157         }
158 
159         template<typename Callback>
Sum(Callback callback,const T & initial)160         T Sum(Callback callback, const T &initial) const
161         {
162             T result = initial;
163             for (int i = 0; i < count_; i++) {
164                 result = callback(result, elements_[i]);
165             }
166             return result;
167         }
168 
Reset()169         void Reset()
170         {
171             start_ = count_ = 0;
172         }
173 
174     private:
175         std::array<T, N> elements_;
176         int start_ {0};
177         int count_ {0};
178     };
179 
180     void NotifyLooperIdleStart(int64_t timestamp, int idleTime);
181     void NotifyLooperIdleEnd(int64_t timestamp);
182     void PostMonitorTask(uint64_t delayMs);
183     void SetStartTimerCallback();
184     void PostLooperTriggerIdleGCTask();
185     void EnableIdleGC(NativeEngine *engine);
186     void UnregisterEnv(NativeEngine *engine);
187 
188 private:
189     double GetCpuUsage() const;
190     bool ShouldTryTriggerGC(int64_t interval);
191     bool CheckLowNotifyState() const;
192     bool CheckLowRunningDurationState() const;
193     bool CheckIntervalIdle(int64_t timestamp, int64_t idleDuration);
194     bool CheckWorkerEnvQueueAllInIdle();
195     void SwitchBackgroundCheckGCTask(int64_t timestamp, int64_t idleDuration);
196     void IntervalMonitor();
197     void NotifyMainThreadTryCompressGC();
198     void NotifyMainThreadTryCompressGCByBackground();
199     void NotifyOneWorkerThreadTryCompressGC();
200     void ClearIdleStats();
201     void TryTriggerGC(TriggerGCType gcType);
202     void PostIdleCheckTask();
203     void CheckWorkerEnvQueue();
204     void StopIdleMonitorTimerTask();
205     void StopIdleMonitorTimerTaskAndPostSleepTask();
206     void CheckShortIdleTask(int64_t timestamp, int idleTime);
207     void PostSwitchBackgroundGCTask();
208     static uint64_t GetIdleMonitoringInterval();
209 
210     static std::shared_ptr<ArkIdleMonitor> instance_;
211 
212     EcmaVM* mainVM_;
213 
214     static constexpr uint32_t IDLE_CHECK_LENGTH = 15;
215     static constexpr uint32_t IDLE_INBACKGROUND_CHECK_LENGTH = 4;
216     static constexpr int IDLE_CHECK_INTERVAL_LENGTH = 4;
217     static constexpr int MIN_TRIGGER_FULLGC_INTERVAL = 90;
218     static constexpr int LOW_IDLE_NOTIFY_THRESHOLD = 10;
219     static constexpr uint64_t SLEEP_MONITORING_INTERVAL = 90 * 1000; // ms
220     static constexpr int64_t MIN_TRIGGER_GC_IDLE_INTERVAL = 10; // ms
221     static constexpr int64_t MAX_TRIGGER_GC_RUNNING_INTERVAL = 1; //ms
222     static constexpr double IDLE_RATIO = 0.985f;
223     static constexpr double SHORT_IDLE_RATIO = 0.96f;
224     static constexpr double BACKGROUND_IDLE_RATIO = 0.85f;
225     static constexpr uint64_t  SHORT_IDLE_DELAY_INTERVAL = 50; // ms;
226     static constexpr double IDLE_CPU_USAGE = 0.5f;
227     static constexpr double IDLE_BACKGROUND_CPU_USAGE = 0.7f;
228     static constexpr int DOUBLE_INTERVAL_CHECK = 2;
229     static constexpr uint32_t IDLE_WORKER_TRIGGER_COUNT = 1; // it needs over IDLE_INBACKGROUND_CHECK_LENGTH
230 
231     std::atomic<bool> idleState_ {false};
232     std::atomic<bool> inBackground_ {true};
233     std::atomic<int64_t> idleNotifyCount_ {0};
234     std::atomic<int64_t> idleStartTimestamp_ {0};
235     std::atomic<int64_t> totalIdleDuration_ {0};
236     int64_t idleEndTimestamp_ {0};
237     int64_t lastTotalIdleDuration_ {0};
238     int64_t startRecordTimestamp_ {0};
239     int64_t intervalTimestamp_ {0};
240     bool started_ {false};
241     bool triggeredGC_ {false};
242     bool needCheckIntervalIdle_ = {true};
243     int currentTimerHandler_ {-1};
244     int waitForStopTimerHandler_ {-1};
245     int switchBackgroundTimerHandler_ {-1};
246     uint32_t numberOfLowIdleNotifyCycles_ {0U};
247     uint32_t numberOfHighIdleTimeRatio_ {0U};
248     std::queue<int> timerHandlerQueue_;
249     uint32_t handlerWaitToStopCount_ {0};
250     RingBuffer<int64_t, IDLE_CHECK_INTERVAL_LENGTH> recordedIdleNotifyInterval_;
251     RingBuffer<int64_t, IDLE_CHECK_INTERVAL_LENGTH> recordedRunningNotifyInterval_;
252     std::mutex timerMutex_;
253     std::mutex queueMutex_;
254     std::queue<napi_env> workerEnvQueue_;
255     static uint64_t gIdleMonitoringInterval;
256     static uint64_t gDelayOverTime;
257 #if defined(ENABLE_EVENT_HANDLER)
258     std::shared_ptr<OHOS::AppExecFwk::EventHandler> mainThreadHandler_ {};
259     static bool gEnableIdleGC;
260 #endif
261 };
262 
263 }
264 
265 #endif /* FOUNDATION_ACE_NAPI_NATIVE_ENGINE_IMPL_ARK_ARK_NATIVE_IDLE_MONITOR_H */