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_WORKER_WORKER_H_ 17 #define JS_CONCURRENT_MODULE_WORKER_WORKER_H_ 18 19 #include <list> 20 #include <map> 21 #include <mutex> 22 23 #include "helper/napi_helper.h" 24 #include "helper/object_helper.h" 25 #include "message_queue.h" 26 #include "napi/native_api.h" 27 #include "napi/native_node_api.h" 28 #include "native_engine/native_engine.h" 29 #include "utils/log.h" 30 #include "worker_runner.h" 31 32 namespace Commonlibrary::Concurrent::WorkerModule { 33 using namespace Commonlibrary::Concurrent::Common::Helper; 34 35 class Worker { 36 public: 37 static const int8_t WORKERPARAMNUM = 2; 38 39 enum RunnerState { STARTING, RUNNING, TERMINATEING, TERMINATED }; 40 enum HostState { ACTIVE, INACTIVE }; 41 enum ListenerMode { ONCE, PERMANENT }; 42 43 enum ScriptMode { CLASSIC, MODULE }; 44 45 struct WorkerListener { WorkerListenerWorkerListener46 WorkerListener(napi_env env, napi_ref callback, ListenerMode mode) 47 : env_(env), callback_(callback), mode_(mode) 48 {} 49 ~WorkerListenerWorkerListener50 ~WorkerListener() 51 { 52 NapiHelper::DeleteReference(env_, callback_); 53 callback_ = nullptr; 54 } 55 NextIsAvailableWorkerListener56 bool NextIsAvailable() const 57 { 58 return mode_ != ONCE; 59 } 60 SetModeWorkerListener61 void SetMode(ListenerMode mode) 62 { 63 mode_ = mode; 64 } 65 66 bool operator==(const WorkerListener& listener) const; 67 68 napi_env env_ {NULL}; 69 napi_ref callback_ {NULL}; 70 ListenerMode mode_ {PERMANENT}; 71 }; 72 73 struct FindWorkerListener { FindWorkerListenerFindWorkerListener74 FindWorkerListener(napi_env env, napi_ref ref) : env_(env), ref_(ref) {} 75 operatorFindWorkerListener76 bool operator()(const WorkerListener* listener) const 77 { 78 napi_value compareObj = NapiHelper::GetReferenceValue(env_, listener->callback_); 79 napi_value obj = NapiHelper::GetReferenceValue(env_, ref_); 80 // the env of listener and cmp listener must be same env because of Synchronization method 81 return NapiHelper::StrictEqual(env_, compareObj, obj); 82 } 83 84 napi_env env_ {nullptr}; 85 napi_ref ref_ {nullptr}; 86 }; 87 88 /** 89 * Creates a worker instance. 90 * 91 * @param env NAPI environment parameters. 92 * @param thisVar URL of the script to be executed by the worker. 93 */ 94 Worker(napi_env env, napi_ref thisVar); 95 96 /** 97 * The destructor of the Worker. 98 */ 99 ~Worker(); 100 101 /** 102 * The host thread receives the information. 103 * 104 * @param req The value of the object passed in by the js layer. 105 */ 106 static void HostOnMessage(const uv_async_t* req); 107 108 /** 109 * The host thread receives the information. 110 * 111 * @param req The value of the object passed in by the js layer. 112 */ 113 static void HostOnError(const uv_async_t* req); 114 115 /** 116 * The worker thread receives the information. 117 * 118 * @param req The value of the object passed in by the js layer. 119 */ 120 static void WorkerOnMessage(const uv_async_t* req); 121 122 /** 123 * ExecuteIn in thread. 124 * 125 * @param data The worker pointer. 126 */ 127 static void ExecuteInThread(const void* data); 128 129 /** 130 * Post a message. 131 * 132 * @param env NAPI environment parameters. 133 * @param thisVar The callback information of the js layer. 134 */ 135 static napi_value PostMessage(napi_env env, napi_callback_info cbinfo); 136 137 /** 138 * Add event listeners to host. 139 * 140 * @param env NAPI environment parameters. 141 * @param cbinfo The callback information of the js layer. 142 */ 143 static napi_value PostMessageToHost(napi_env env, napi_callback_info cbinfo); 144 145 /** 146 * Terminates the worker thread to stop the worker from receiving messages. 147 * 148 * @param env NAPI environment parameters. 149 * @param cbinfo The callback information of the js layer. 150 */ 151 static napi_value Terminate(napi_env env, napi_callback_info cbinfo); 152 153 /** 154 * Close the worker. 155 * 156 * @param env NAPI environment parameters. 157 * @param cbinfo The callback information of the js layer. 158 */ 159 static napi_value CloseWorker(napi_env env, napi_callback_info cbinfo); 160 161 /** 162 * Adds an event listener to the worker. 163 * 164 * @param env NAPI environment parameters. 165 * @param cbinfo The callback information of the js layer. 166 */ 167 static napi_value On(napi_env env, napi_callback_info cbinfo); 168 169 /** 170 * Adds an event listener to the worker and removes the event listener automatically after it is invoked once. 171 * 172 * @param env NAPI environment parameters. 173 * @param cbinfo The callback information of the js layer. 174 */ 175 static napi_value Once(napi_env env, napi_callback_info cbinfo); 176 177 /** 178 * Removes an event listener to the worker. 179 * 180 * @param env NAPI environment parameters. 181 * @param cbinfo The callback information of the js layer. 182 */ 183 static napi_value Off(napi_env env, napi_callback_info cbinfo); 184 185 /** 186 * Add event listeners. 187 * 188 * @param env NAPI environment parameters. 189 * @param cbinfo The callback information of the js layer. 190 */ 191 static napi_value AddEventListener(napi_env env, napi_callback_info cbinfo); 192 193 /** 194 * Dispatch the event. 195 * 196 * @param env NAPI environment parameters. 197 * @param cbinfo The callback information of the js layer. 198 */ 199 static napi_value DispatchEvent(napi_env env, napi_callback_info cbinfo); 200 201 /** 202 * Remove the event listener. 203 * 204 * @param env NAPI environment parameters. 205 * @param cbinfo The callback information of the js layer. 206 */ 207 static napi_value RemoveEventListener(napi_env env, napi_callback_info cbinfo); 208 209 /** 210 * Remove all listener. 211 * 212 * @param env NAPI environment parameters. 213 * @param cbinfo The callback information of the js layer. 214 */ 215 static napi_value RemoveAllListener(napi_env env, napi_callback_info cbinfo); 216 217 /** 218 * Add the listener. 219 * 220 * @param env NAPI environment parameters. 221 * @param cbinfo The callback information of the js layer. 222 */ 223 static napi_value AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode); 224 225 /** 226 * Remove the listener. 227 * 228 * @param env NAPI environment parameters. 229 * @param cbinfo The callback information of the js layer. 230 */ 231 static napi_value RemoveListener(napi_env env, napi_callback_info cbinfo); 232 233 /** 234 * The constructor of worker. 235 * 236 * @param env NAPI environment parameters. 237 * @param cbinfo The callback information of the js layer. 238 */ 239 static napi_value WorkerConstructor(napi_env env, napi_callback_info cbinfo); 240 241 /** 242 * Initialize the worker. 243 * 244 * @param env NAPI environment parameters. 245 * @param cbinfo The callback information of the js layer. 246 */ 247 static napi_value InitWorker(napi_env env, napi_value exports); 248 249 /** 250 * Cancel the task. 251 * 252 * @param env NAPI environment parameters. 253 * @param cbinfo The callback information of the js layer. 254 */ 255 static napi_value CancelTask(napi_env env, napi_callback_info cbinfo); 256 257 /** 258 * The parent port cancels the task. 259 * 260 * @param env NAPI environment parameters. 261 * @param cbinfo The callback information of the js layer. 262 */ 263 static napi_value ParentPortCancelTask(napi_env env, napi_callback_info cbinfo); 264 265 /** 266 * The parent port adds an event listener. 267 * 268 * @param env NAPI environment parameters. 269 * @param cbinfo The callback information of the js layer. 270 */ 271 static napi_value ParentPortAddEventListener(napi_env env, napi_callback_info cbinfo); 272 273 /** 274 * The parent port removes all event listener. 275 * 276 * @param env NAPI environment parameters. 277 * @param cbinfo The callback information of the js layer. 278 */ 279 static napi_value ParentPortRemoveAllListener(napi_env env, napi_callback_info cbinfo); 280 281 /** 282 * The parent port dispatch the event listener. 283 * 284 * @param env NAPI environment parameters. 285 * @param cbinfo The callback information of the js layer. 286 */ 287 static napi_value ParentPortDispatchEvent(napi_env env, napi_callback_info cbinfo); 288 289 /** 290 * The parent port removes the event listener. 291 * 292 * @param env NAPI environment parameters. 293 * @param cbinfo The callback information of the js layer. 294 */ 295 static napi_value ParentPortRemoveEventListener(napi_env env, napi_callback_info cbinfo); 296 297 void StartExecuteInThread(napi_env env, const char* script); 298 299 bool UpdateWorkerState(RunnerState state); 300 bool UpdateHostState(HostState state); 301 IsRunning()302 bool IsRunning() const 303 { 304 return runnerState_.load(std::memory_order_acquire) == RUNNING; 305 } 306 IsTerminated()307 bool IsTerminated() const 308 { 309 return runnerState_.load(std::memory_order_acquire) >= TERMINATED; 310 } 311 IsTerminating()312 bool IsTerminating() const 313 { 314 return runnerState_.load(std::memory_order_acquire) == TERMINATEING; 315 } 316 SetScriptMode(ScriptMode mode)317 void SetScriptMode(ScriptMode mode) 318 { 319 scriptMode_ = mode; 320 } 321 322 void AddListenerInner(napi_env env, const char* type, const WorkerListener* listener); 323 void RemoveListenerInner(napi_env env, const char* type, napi_ref callback); 324 void RemoveAllListenerInner(); 325 GetWorkerLoop()326 uv_loop_t* GetWorkerLoop() const 327 { 328 if (workerEnv_ != nullptr) { 329 return NapiHelper::GetLibUV(workerEnv_); 330 } 331 return nullptr; 332 } 333 SetWorkerEnv(napi_env workerEnv)334 void SetWorkerEnv(napi_env workerEnv) 335 { 336 workerEnv_ = workerEnv; 337 } 338 GetScript()339 std::string GetScript() const 340 { 341 return script_; 342 } 343 GetName()344 std::string GetName() const 345 { 346 return name_; 347 } 348 ClearWorkerTasks()349 bool ClearWorkerTasks() 350 { 351 if (hostEnv_ != nullptr) { 352 workerMessageQueue_.Clear(hostEnv_); 353 return true; 354 } 355 return false; 356 } 357 HostIsStop()358 bool HostIsStop() const 359 { 360 return hostState_.load(std::memory_order_acquire) == INACTIVE; 361 } 362 IsSameWorkerEnv(napi_env env)363 bool IsSameWorkerEnv(napi_env env) const 364 { 365 return workerEnv_ == env; 366 } 367 Loop()368 void Loop() const 369 { 370 uv_loop_t* loop = GetWorkerLoop(); 371 if (loop != nullptr) { 372 uv_run(loop, UV_RUN_DEFAULT); 373 } else { 374 HILOG_ERROR("worker:: Worker loop is nullptr when start worker loop"); 375 return; 376 } 377 } 378 379 private: 380 void WorkerOnMessageInner(); 381 void HostOnMessageInner(); 382 void HostOnErrorInner(); 383 void HostOnMessageErrorInner(); 384 void WorkerOnMessageErrorInner(); 385 void WorkerOnErrorInner(napi_value error); 386 387 void HandleException(); 388 bool CallWorkerFunction(size_t argc, const napi_value* argv, const char* methodName, bool tryCatch); 389 void CallHostFunction(size_t argc, const napi_value* argv, const char* methodName) const; 390 391 void HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type); 392 void ParentPortHandleEventListeners(napi_env env, napi_value recv, 393 size_t argc, const napi_value* argv, const char* type); 394 void TerminateInner(); 395 396 void PostMessageInner(MessageDataType data); 397 void PostMessageToHostInner(MessageDataType data); 398 399 void TerminateWorker(); 400 void CloseInner(); 401 402 void PublishWorkerOverSignal(); 403 void CloseWorkerCallback(); 404 void CloseHostCallback() const; 405 406 void ReleaseWorkerThreadContent(); 407 void ReleaseHostThreadContent(); 408 bool PrepareForWorkerInstance(); 409 void ParentPortAddListenerInner(napi_env env, const char* type, const WorkerListener* listener); 410 void ParentPortRemoveAllListenerInner(); 411 void ParentPortRemoveListenerInner(napi_env env, const char* type, napi_ref callback); 412 void PreparePandafile(); 413 void GetContainerScopeId(napi_env env); 414 415 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 416 static void HandleDebuggerTask(const uv_async_t* req); 417 void DebuggerOnPostTask(std::function<void()>&& task); 418 #endif 419 GetHostEnv()420 napi_env GetHostEnv() const 421 { 422 return hostEnv_; 423 } 424 GetWorkerEnv()425 napi_env GetWorkerEnv() const 426 { 427 return workerEnv_; 428 } 429 430 std::string script_ {}; 431 std::string name_ {}; 432 ScriptMode scriptMode_ {CLASSIC}; 433 int32_t scopeId_ {-1}; 434 435 MessageQueue workerMessageQueue_ {}; 436 MessageQueue hostMessageQueue_ {}; 437 MessageQueue errorQueue_ {}; 438 439 uv_async_t* workerOnMessageSignal_ = nullptr; 440 uv_async_t* hostOnMessageSignal_ = nullptr; 441 uv_async_t* hostOnErrorSignal_ = nullptr; 442 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 443 uv_async_t debuggerOnPostTaskSignal_ {}; 444 std::function<void()> debuggerTask_; 445 #endif 446 447 std::atomic<RunnerState> runnerState_ {STARTING}; 448 std::atomic<HostState> hostState_ {ACTIVE}; 449 std::unique_ptr<WorkerRunner> runner_ {}; 450 451 napi_env hostEnv_ {nullptr}; 452 napi_env workerEnv_ {nullptr}; 453 454 napi_ref workerRef_ {nullptr}; 455 napi_ref parentPort_ {nullptr}; 456 457 std::map<std::string, std::list<WorkerListener*>> eventListeners_ {}; 458 std::map<std::string, std::list<WorkerListener*>> parentPortEventListeners_ {}; 459 460 std::recursive_mutex liveStatusLock_ {}; 461 }; 462 } // namespace Commonlibrary::Concurrent::WorkerModule 463 #endif // JS_CONCURRENT_MODULE_WORKER_WORKER_H_ 464