1 /* 2 * Copyright (c) 2022 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 JS_CONCURRENT_MODULE_TASKPOOL_WORKER_H 17 #define JS_CONCURRENT_MODULE_TASKPOOL_WORKER_H 18 19 #include <mutex> 20 21 #if defined(ENABLE_TASKPOOL_FFRT) 22 #include "cpp/task.h" 23 #endif 24 #include "helper/concurrent_helper.h" 25 #include "helper/error_helper.h" 26 #include "helper/napi_helper.h" 27 #include "helper/object_helper.h" 28 #include "napi/native_api.h" 29 #include "napi/native_node_api.h" 30 #include "native_engine/native_engine.h" 31 #include "qos_helper.h" 32 #include "task.h" 33 #include "task_runner.h" 34 #include "tools/log.h" 35 36 namespace Commonlibrary::Concurrent::TaskPoolModule { 37 using namespace Commonlibrary::Concurrent::Common; 38 using namespace Commonlibrary::Concurrent::Common::Helper; 39 using namespace Commonlibrary::Platform; 40 41 enum class WorkerState { IDLE, RUNNING, BLOCKED }; 42 43 #if defined(ENABLE_TASKPOOL_FFRT) 44 static const std::map<Priority, int> WORKERPRIORITY_FFRTQOS_MAP = { 45 {Priority::IDLE, ffrt::qos_background}, 46 {Priority::LOW, ffrt::qos_utility}, 47 {Priority::MEDIUM, ffrt::qos_default}, 48 {Priority::HIGH, ffrt::qos_user_initiated}, 49 }; 50 #endif 51 52 struct ThreadInfo { 53 Priority priority {Priority::DEFAULT}; 54 pid_t tid = 0; 55 std::vector<uint32_t> currentTaskId {}; 56 }; 57 58 class Worker { 59 public: 60 using DebuggerPostTask = std::function<void()>; 61 62 static Worker* WorkerConstructor(napi_env env); 63 64 void NotifyExecuteTask(); 65 66 void NotifyTaskBegin(); 67 // the function will only be called when the task is finished or 68 // exits abnormally, so we can not put it in the scope directly 69 void NotifyTaskFinished(); 70 71 Priority GetPriority() const; 72 73 static void NotifyTaskResult(napi_env env, Task* task, napi_value result); 74 static void NotifyHandleTaskResult(Task* task); 75 76 #if defined(ENABLE_TASKPOOL_FFRT) 77 bool IsLoopActive(); 78 uint64_t GetWaitTime(); 79 #endif 80 #if defined(ENABLE_TASKPOOL_HISYSEVENT) 81 bool IsNeedReport(uint64_t intervalTime); 82 void IncreaseReportCount(); 83 #endif 84 85 private: Worker(napi_env env)86 explicit Worker(napi_env env) : hostEnv_(env) {}; 87 88 ~Worker() = default; 89 90 Worker(const Worker &) = delete; 91 Worker& operator=(const Worker &) = delete; 92 Worker(Worker &&) = delete; 93 Worker& operator=(Worker &&) = delete; 94 95 void NotifyIdle(); 96 void NotifyWorkerCreated(); NotifyTaskRunning()97 void NotifyTaskRunning() 98 { 99 state_ = WorkerState::RUNNING; 100 startTime_ = ConcurrentHelper::GetMilliseconds(); 101 runningCount_++; 102 } 103 HasRunningTasks()104 bool HasRunningTasks() const 105 { 106 return runningCount_ != 0; 107 } 108 109 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 110 static void HandleDebuggerTask(const uv_async_t* req); 111 void DebuggerOnPostTask(std::function<void()>&& task); 112 #endif 113 GetWorkerLoop()114 uv_loop_t* GetWorkerLoop() const 115 { 116 if (workerEnv_ != nullptr) { 117 return NapiHelper::GetLibUV(workerEnv_); 118 } 119 return nullptr; 120 } 121 RunLoop()122 void RunLoop() const 123 { 124 uv_loop_t* loop = GetWorkerLoop(); 125 if (loop != nullptr) { 126 uv_run(loop, UV_RUN_DEFAULT); 127 } else { 128 HILOG_ERROR("taskpool:: Worker loop is nullptr when start worker loop"); 129 } 130 } 131 132 // we will use the scope to manage resources automatically, 133 // including the HandleScope and NotifyRunning/NotifyIdle 134 class RunningScope { 135 public: RunningScope(Worker * worker)136 explicit RunningScope(Worker* worker) : worker_(worker) 137 { 138 napi_open_handle_scope(worker_->workerEnv_, &scope_); 139 worker_->idleState_ = false; 140 worker->isExecutingLongTask_ = false; 141 worker_->NotifyTaskRunning(); 142 } 143 144 ~RunningScope(); 145 146 private: 147 Worker* worker_ = nullptr; 148 napi_handle_scope scope_ = nullptr; 149 }; 150 151 // use PriorityScope to manage the priority setting of workers 152 // reset qos_user_initiated when exit PriorityScope 153 class PriorityScope { 154 public: 155 PriorityScope(Worker* worker, Priority taskPriority); ~PriorityScope()156 ~PriorityScope() 157 { 158 worker_->ResetPerformIdleState(); 159 worker_->ResetWorkerPriority(); 160 } 161 162 private: 163 Worker* worker_ = nullptr; 164 }; 165 166 void StartExecuteInThread(); 167 static void ExecuteInThread(const void* data); 168 bool PrepareForWorkerInstance(); 169 void ReleaseWorkerThreadContent(); 170 void ResetWorkerPriority(); 171 bool CheckFreeConditions(); 172 bool UpdateWorkerState(WorkerState expect, WorkerState desired); 173 void StoreTaskId(uint32_t taskId); 174 bool InitTaskPoolFunc(napi_env env, napi_value func, Task* task); 175 void UpdateExecutedInfo(); 176 void UpdateLongTaskInfo(Task* task); 177 bool IsExecutingLongTask(); 178 bool HasLongTask(); 179 void TerminateTask(uint32_t taskId); 180 void CloseHandles(); 181 void PostReleaseSignal(); 182 bool IsRunnable(uint64_t currTime) const; 183 void UpdateWorkerWakeUpTime(); 184 void EraseRunningTaskId(uint32_t taskId); 185 186 static void HandleFunctionResult(napi_env env, Task* task); 187 static void PerformTask(const uv_async_t* req); 188 static void TaskResultCallback(napi_env env, napi_value result, bool success, void* data); 189 static void ReleaseWorkerHandles(const uv_async_t* req); 190 static void TriggerGCCheck(const uv_async_t* req); 191 192 #if defined(ENABLE_TASKPOOL_FFRT) 193 void InitFfrtInfo(); 194 void InitLoopHandleNum(); 195 #endif 196 void ResetPerformIdleState(); 197 198 napi_env hostEnv_ {nullptr}; 199 napi_env workerEnv_ {nullptr}; 200 uv_async_t* performTaskSignal_ {nullptr}; 201 uv_async_t* clearWorkerSignal_ {nullptr}; 202 uv_async_t* triggerGCCheckSignal_ {nullptr}; 203 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 204 uv_async_t* debuggerOnPostTaskSignal_ {nullptr}; 205 std::mutex debuggerMutex_; 206 std::queue<DebuggerPostTask> debuggerQueue_ {}; 207 #endif 208 std::unique_ptr<TaskRunner> runner_ {nullptr}; 209 210 std::atomic<int32_t> runningCount_ = 0; 211 std::atomic<bool> idleState_ = true; // true means the worker is idle 212 std::atomic<uint64_t> idlePoint_ = ConcurrentHelper::GetMilliseconds(); 213 std::atomic<uint64_t> startTime_ = ConcurrentHelper::GetMilliseconds(); 214 std::atomic<uint64_t> wakeUpTime_ = ConcurrentHelper::GetMilliseconds(); 215 std::atomic<WorkerState> state_ {WorkerState::IDLE}; 216 std::atomic<bool> hasExecuted_ = false; // false means this worker hasn't execute any tasks 217 Priority priority_ {Priority::DEFAULT}; 218 pid_t tid_ = 0; 219 std::vector<uint32_t> currentTaskId_ {}; 220 std::mutex currentTaskIdMutex_; 221 uint64_t lastCpuTime_ = 0; 222 uint32_t idleCount_ = 0; 223 std::atomic<bool> hasLongTask_ = false; 224 std::atomic<bool> isExecutingLongTask_ = false; 225 std::mutex longMutex_; 226 std::unordered_set<uint32_t> longTasksSet_ {}; 227 friend class TaskManager; 228 friend class NativeEngineTest; 229 230 #if defined(ENABLE_TASKPOOL_FFRT) 231 void* ffrtTaskHandle_ = nullptr; 232 uint32_t initActiveHandleNum_ = 0; 233 #endif 234 #if defined(ENABLE_TASKPOOL_HISYSEVENT) 235 std::atomic<int32_t> reportCount_ = 0; 236 #endif 237 }; 238 } // namespace Commonlibrary::Concurrent::TaskPoolModule 239 #endif // JS_CONCURRENT_MODULE_TASKPOOL_WORKER_H