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 */