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