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