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 <condition_variable> 20 #include <list> 21 #include <map> 22 #include <mutex> 23 24 #include "helper/napi_helper.h" 25 #include "helper/object_helper.h" 26 #include "message_queue.h" 27 #include "napi/native_api.h" 28 #include "napi/native_node_api.h" 29 #include "native_engine/native_engine.h" 30 #include "native_engine/worker_manager.h" 31 #include "worker_runner.h" 32 #if defined(ENABLE_WORKER_EVENTHANDLER) 33 #include "event_handler.h" 34 #endif 35 36 namespace Commonlibrary::Concurrent::WorkerModule { 37 using namespace Commonlibrary::Concurrent::Common::Helper; 38 39 class Worker { 40 public: 41 enum RunnerState { STARTING, RUNNING, TERMINATEING, TERMINATED }; 42 enum HostState { ACTIVE, INACTIVE }; 43 enum ListenerMode { ONCE, PERMANENT }; 44 enum ScriptMode { CLASSIC, MODULE }; 45 46 using DebuggerPostTask = std::function<void()>; 47 48 struct WorkerListener { WorkerListenerWorkerListener49 WorkerListener(napi_env env, napi_ref callback, ListenerMode mode) 50 : env_(env), callback_(callback), mode_(mode) 51 {} 52 ~WorkerListenerWorkerListener53 ~WorkerListener() 54 { 55 NapiHelper::DeleteReference(env_, callback_); 56 callback_ = nullptr; 57 } 58 NextIsAvailableWorkerListener59 bool NextIsAvailable() const 60 { 61 return mode_ != ONCE; 62 } 63 SetModeWorkerListener64 void SetMode(ListenerMode mode) 65 { 66 mode_ = mode; 67 } 68 69 bool operator==(const WorkerListener& listener) const; 70 71 napi_env env_ {NULL}; 72 napi_ref callback_ {NULL}; 73 ListenerMode mode_ {PERMANENT}; 74 }; 75 76 struct FindWorkerListener { FindWorkerListenerFindWorkerListener77 FindWorkerListener(napi_env env, napi_ref ref) : env_(env), ref_(ref) {} 78 operatorFindWorkerListener79 bool operator()(const WorkerListener* listener) const 80 { 81 napi_value compareObj = NapiHelper::GetReferenceValue(env_, listener->callback_); 82 napi_value obj = NapiHelper::GetReferenceValue(env_, ref_); 83 // the env of listener and cmp listener must be same env because of Synchronization method 84 return NapiHelper::StrictEqual(env_, compareObj, obj); 85 } 86 87 napi_env env_ {nullptr}; 88 napi_ref ref_ {nullptr}; 89 }; 90 91 struct WorkerParams { 92 std::string name_ {}; 93 ScriptMode type_ {CLASSIC}; 94 }; 95 96 struct WorkerWrapper { WorkerWrapperWorkerWrapper97 explicit WorkerWrapper(Worker* worker) : workerPtr_(worker) {} 98 GetWorkerWorkerWrapper99 Worker* GetWorker() const 100 { 101 return workerPtr_; 102 } 103 104 Worker* workerPtr_ {nullptr}; 105 }; 106 107 /** 108 * Creates a worker instance. 109 * 110 * @param env NAPI environment parameters. 111 * @param thisVar URL of the script to be executed by the worker. 112 */ 113 Worker(napi_env env, napi_ref thisVar); 114 115 /** 116 * The destructor of the Worker. 117 */ 118 ~Worker(); 119 120 /** 121 * The host thread receives the information. 122 * 123 * @param req The value of the object passed in by the js layer. 124 */ 125 static void HostOnMessage(const uv_async_t* req); 126 127 /** 128 * The host thread receives the information. 129 * 130 * @param req The value of the object passed in by the js layer. 131 */ 132 static void HostOnError(const uv_async_t* req); 133 134 /** 135 * The worker thread receives the information. 136 * 137 * @param req The value of the object passed in by the js layer. 138 */ 139 static void WorkerOnMessage(const uv_async_t* req); 140 141 /** 142 * ExecuteIn in thread. 143 * 144 * @param data The worker pointer. 145 */ 146 static void ExecuteInThread(const void* data); 147 148 /** 149 * Post a message. 150 * 151 * @param env NAPI environment parameters. 152 * @param thisVar The callback information of the js layer. 153 */ 154 static napi_value PostMessage(napi_env env, napi_callback_info cbinfo); 155 156 /** 157 * Post a message, if has sendable objects in it pass sendable objects' reference. 158 * 159 * @param env NAPI environment parameters. 160 * @param thisVar The callback information of the js layer. 161 */ 162 static napi_value PostMessageWithSharedSendable(napi_env env, napi_callback_info cbinfo); 163 164 /** 165 * postMessage implementation 166 * 167 * @param env NAPI environment parameters. 168 * @param thisVar The callback information of the js layer. 169 */ 170 static napi_value CommonPostMessage(napi_env env, napi_callback_info cbinfo, bool cloneSendable); 171 172 /** 173 * Add event listeners to host. 174 * 175 * @param env NAPI environment parameters. 176 * @param cbinfo The callback information of the js layer. 177 */ 178 static napi_value PostMessageToHost(napi_env env, napi_callback_info cbinfo); 179 180 /** 181 * Post a message, if has sendable objects in it pass sendable objects' reference. 182 * 183 * @param env NAPI environment parameters. 184 * @param thisVar The callback information of the js layer. 185 */ 186 static napi_value PostMessageWithSharedSendableToHost(napi_env env, napi_callback_info cbinfo); 187 188 /** 189 * postMessage implementation 190 * 191 * @param env NAPI environment parameters. 192 * @param thisVar The callback information of the js layer. 193 */ 194 static napi_value CommonPostMessageToHost(napi_env env, napi_callback_info cbinfo, bool cloneSendable); 195 196 /** 197 * Terminates the worker thread to stop the worker from receiving messages. 198 * 199 * @param env NAPI environment parameters. 200 * @param cbinfo The callback information of the js layer. 201 */ 202 static napi_value Terminate(napi_env env, napi_callback_info cbinfo); 203 204 /** 205 * Close the worker. 206 * 207 * @param env NAPI environment parameters. 208 * @param cbinfo The callback information of the js layer. 209 */ 210 static napi_value CloseWorker(napi_env env, napi_callback_info cbinfo); 211 212 /** 213 * Adds an event listener to the worker. 214 * 215 * @param env NAPI environment parameters. 216 * @param cbinfo The callback information of the js layer. 217 */ 218 static napi_value On(napi_env env, napi_callback_info cbinfo); 219 220 /** 221 * Adds an event listener to the worker and removes the event listener automatically after it is invoked once. 222 * 223 * @param env NAPI environment parameters. 224 * @param cbinfo The callback information of the js layer. 225 */ 226 static napi_value Once(napi_env env, napi_callback_info cbinfo); 227 228 /** 229 * Removes an event listener to the worker. 230 * 231 * @param env NAPI environment parameters. 232 * @param cbinfo The callback information of the js layer. 233 */ 234 static napi_value Off(napi_env env, napi_callback_info cbinfo); 235 236 /** 237 * Add event listeners. 238 * 239 * @param env NAPI environment parameters. 240 * @param cbinfo The callback information of the js layer. 241 */ 242 static napi_value AddEventListener(napi_env env, napi_callback_info cbinfo); 243 244 /** 245 * Dispatch the event. 246 * 247 * @param env NAPI environment parameters. 248 * @param cbinfo The callback information of the js layer. 249 */ 250 static napi_value DispatchEvent(napi_env env, napi_callback_info cbinfo); 251 252 /** 253 * Remove the event listener. 254 * 255 * @param env NAPI environment parameters. 256 * @param cbinfo The callback information of the js layer. 257 */ 258 static napi_value RemoveEventListener(napi_env env, napi_callback_info cbinfo); 259 260 /** 261 * Remove all listener. 262 * 263 * @param env NAPI environment parameters. 264 * @param cbinfo The callback information of the js layer. 265 */ 266 static napi_value RemoveAllListener(napi_env env, napi_callback_info cbinfo); 267 268 /** 269 * Add the listener. 270 * 271 * @param env NAPI environment parameters. 272 * @param cbinfo The callback information of the js layer. 273 */ 274 static napi_value AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode); 275 276 /** 277 * Remove the listener. 278 * 279 * @param env NAPI environment parameters. 280 * @param cbinfo The callback information of the js layer. 281 */ 282 static napi_value RemoveListener(napi_env env, napi_callback_info cbinfo); 283 284 /** 285 * The constructor of worker. 286 * 287 * @param env NAPI environment parameters. 288 * @param cbinfo The callback information of the js layer. 289 */ 290 static napi_value LimitedWorkerConstructor(napi_env env, napi_callback_info cbinfo); 291 static napi_value ThreadWorkerConstructor(napi_env env, napi_callback_info cbinfo); 292 static napi_value WorkerConstructor(napi_env env, napi_callback_info cbinfo); 293 static napi_value Constructor(napi_env env, napi_callback_info cbinfo, bool limitSign = false, 294 WorkerVersion version = WorkerVersion::NONE); 295 296 /** 297 * Initialize the worker and port. 298 * 299 * @param env NAPI environment parameters. 300 * @param cbinfo The callback information of the js layer. 301 */ 302 static napi_value InitWorker(napi_env env, napi_value exports); 303 static napi_value InitPort(napi_env env, napi_value exports); 304 305 /** 306 * Cancel the task. 307 * 308 * @param env NAPI environment parameters. 309 * @param cbinfo The callback information of the js layer. 310 */ 311 static napi_value CancelTask(napi_env env, napi_callback_info cbinfo); 312 313 /** 314 * The parent port cancels the task. 315 * 316 * @param env NAPI environment parameters. 317 * @param cbinfo The callback information of the js layer. 318 */ 319 static napi_value ParentPortCancelTask(napi_env env, napi_callback_info cbinfo); 320 321 /** 322 * The parent port adds an event listener. 323 * 324 * @param env NAPI environment parameters. 325 * @param cbinfo The callback information of the js layer. 326 */ 327 static napi_value ParentPortAddEventListener(napi_env env, napi_callback_info cbinfo); 328 329 /** 330 * The parent port removes all event listener. 331 * 332 * @param env NAPI environment parameters. 333 * @param cbinfo The callback information of the js layer. 334 */ 335 static napi_value ParentPortRemoveAllListener(napi_env env, napi_callback_info cbinfo); 336 337 /** 338 * The parent port dispatch the event listener. 339 * 340 * @param env NAPI environment parameters. 341 * @param cbinfo The callback information of the js layer. 342 */ 343 static napi_value ParentPortDispatchEvent(napi_env env, napi_callback_info cbinfo); 344 345 /** 346 * The parent port removes the event listener. 347 * 348 * @param env NAPI environment parameters. 349 * @param cbinfo The callback information of the js layer. 350 */ 351 static napi_value ParentPortRemoveEventListener(napi_env env, napi_callback_info cbinfo); 352 353 /** 354 * Register a globalCallObject on host side. 355 * 356 * @param env NAPI environment parameters. 357 * @param cbinfo The callback information of the js layer. 358 */ 359 static napi_value RegisterGlobalCallObject(napi_env env, napi_callback_info cbinfo); 360 361 /** 362 * Unregister the specific globalCallObject on host side. 363 * 364 * @param env NAPI environment parameters. 365 * @param cbinfo The callback information of the js layer. 366 */ 367 static napi_value UnregisterGlobalCallObject(napi_env env, napi_callback_info cbinfo); 368 369 /** 370 * Post a global synchronized call request to an object registered on host side. 371 * 372 * @param env NAPI environment parameters. 373 * @param cbinfo The callback information of the js layer. 374 */ 375 static napi_value GlobalCall(napi_env env, napi_callback_info cbinfo); 376 377 static void HostOnGlobalCall(const uv_async_t* req); 378 379 static bool CanCreateWorker(napi_env env, WorkerVersion target); 380 381 static WorkerParams* CheckWorkerArgs(napi_env env, napi_value argsValue); 382 383 static void WorkerThrowError(napi_env env, int32_t errCode, const char* errMessage = nullptr); 384 385 static void WorkerDestructor(napi_env env, void* data, void* hint); 386 static void WorkerHostEnvCleanCallback(void* data); 387 static void LimitedWorkerHostEnvCleanCallback(void* data); 388 389 #if defined(ENABLE_WORKER_EVENTHANDLER) 390 static std::shared_ptr<OHOS::AppExecFwk::EventHandler> GetMainThreadHandler(); 391 #endif 392 393 void StartExecuteInThread(napi_env env, const char* script); 394 395 bool UpdateWorkerState(RunnerState state); 396 bool UpdateHostState(HostState state); 397 IsNotTerminate()398 bool IsNotTerminate() const 399 { 400 return runnerState_.load(std::memory_order_acquire) <= RUNNING; 401 } 402 IsRunning()403 bool IsRunning() const 404 { 405 return runnerState_.load(std::memory_order_acquire) == RUNNING; 406 } 407 IsTerminated()408 bool IsTerminated() const 409 { 410 return runnerState_.load(std::memory_order_acquire) >= TERMINATED; 411 } 412 IsTerminating()413 bool IsTerminating() const 414 { 415 return runnerState_.load(std::memory_order_acquire) == TERMINATEING; 416 } 417 SetScriptMode(ScriptMode mode)418 void SetScriptMode(ScriptMode mode) 419 { 420 scriptMode_ = mode; 421 } 422 423 void AddListenerInner(napi_env env, const char* type, const WorkerListener* listener); 424 void RemoveListenerInner(napi_env env, const char* type, napi_ref callback); 425 void RemoveAllListenerInner(); 426 void EraseWorker(); GetWorkerLoop()427 uv_loop_t* GetWorkerLoop() const 428 { 429 if (workerEnv_ != nullptr) { 430 return NapiHelper::GetLibUV(workerEnv_); 431 } 432 return nullptr; 433 } 434 SetWorkerEnv(napi_env workerEnv)435 void SetWorkerEnv(napi_env workerEnv) 436 { 437 workerEnv_ = workerEnv; 438 if (workerEnvCallback_) { 439 workerEnvCallback_(workerEnv_); 440 } 441 } 442 GetScript()443 std::string GetScript() const 444 { 445 return script_; 446 } 447 GetName()448 std::string GetName() const 449 { 450 return name_; 451 } 452 ClearWorkerTasks()453 bool ClearWorkerTasks() 454 { 455 if (hostEnv_ != nullptr) { 456 workerMessageQueue_.Clear(hostEnv_); 457 return true; 458 } 459 return false; 460 } 461 HostIsStop()462 bool HostIsStop() const 463 { 464 return hostState_.load(std::memory_order_acquire) == INACTIVE; 465 } 466 IsSameWorkerEnv(napi_env env)467 bool IsSameWorkerEnv(napi_env env) const 468 { 469 return workerEnv_ == env; 470 } 471 Loop()472 void Loop() const 473 { 474 uv_loop_t* loop = GetWorkerLoop(); 475 if (loop != nullptr) { 476 uv_run(loop, UV_RUN_DEFAULT); 477 } else { 478 return; 479 } 480 } 481 RegisterCallbackForWorkerEnv(std::function<void (napi_env)> callback)482 void RegisterCallbackForWorkerEnv(std::function<void (napi_env)> callback) 483 { 484 workerEnvCallback_ = callback; 485 if (workerEnv_ != nullptr) { 486 workerEnvCallback_(workerEnv_); 487 } 488 } 489 GetWorkerEnv()490 napi_env GetWorkerEnv() const 491 { 492 return workerEnv_; 493 } 494 GetHostEnv()495 napi_env GetHostEnv() const 496 { 497 return hostEnv_; 498 } 499 500 private: 501 void WorkerOnMessageInner(); 502 void HostOnMessageInner(); 503 void HostOnErrorInner(); 504 void HostOnMessageErrorInner(); 505 void HostOnGlobalCallInner(); 506 void WorkerOnMessageErrorInner(); 507 void WorkerOnErrorInner(napi_value error); 508 509 void HandleHostException() const; 510 void HandleException(); 511 void HandleUncaughtException(napi_value exception); 512 bool CallWorkerFunction(size_t argc, const napi_value* argv, const char* methodName, bool tryCatch); 513 void CallHostFunction(size_t argc, const napi_value* argv, const char* methodName) const; 514 515 bool HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type); 516 void ParentPortHandleEventListeners(napi_env env, napi_value recv, size_t argc, 517 const napi_value* argv, const char* type, bool tryCatch); 518 void TerminateInner(); 519 520 void PostMessageInner(MessageDataType data); 521 void PostMessageToHostInner(MessageDataType data); 522 523 void TerminateWorker(); 524 525 void CloseInner(); 526 527 void PublishWorkerOverSignal(); 528 void CloseWorkerCallback(); 529 void CloseHostCallback(); 530 531 void PostWorkerOverTask(); 532 void PostWorkerErrorTask(); 533 void PostWorkerMessageTask(); 534 void PostWorkerGlobalCallTask(); 535 static bool IsValidWorker(Worker* worker); 536 static bool IsValidLimitedWorker(Worker* limitedWorker); 537 static void HostEnvCleanCallbackInner(Worker* worker); 538 539 void InitHostHandle(uv_loop_t* loop); 540 void CloseHostHandle(); 541 542 void ReleaseWorkerThreadContent(); 543 void ReleaseHostThreadContent(); 544 bool PrepareForWorkerInstance(); 545 void ParentPortAddListenerInner(napi_env env, const char* type, const WorkerListener* listener); 546 void ParentPortRemoveAllListenerInner(); 547 void ParentPortRemoveListenerInner(napi_env env, const char* type, napi_ref callback); 548 void GetContainerScopeId(napi_env env); 549 550 void AddGlobalCallObject(const std::string &instanceName, napi_ref obj); 551 bool RemoveGlobalCallObject(const std::string &instanceName); 552 void ClearGlobalCallObject(); 553 void AddGlobalCallError(int32_t errCode, napi_value errData = nullptr); 554 void HandleGlobalCallError(napi_env env); 555 void ClearGlobalCallError(napi_env env); 556 void InitGlobalCallStatus(napi_env env); 557 void IncreaseGlobalCallId(); 558 559 void ClearHostMessage(napi_env env); 560 561 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 562 static void HandleDebuggerTask(const uv_async_t* req); 563 void DebuggerOnPostTask(std::function<void()>&& task); 564 #endif 565 566 std::string script_ {}; 567 std::string fileName_ {}; 568 std::string name_ {}; 569 ScriptMode scriptMode_ {CLASSIC}; 570 WorkerType workerType_ {WorkerType::THREAD_WORKER}; 571 bool isLimitedWorker_ {false}; 572 bool isRelativePath_ {false}; 573 int32_t scopeId_ {-1}; 574 575 MessageQueue workerMessageQueue_ {}; 576 MessageQueue hostMessageQueue_ {}; 577 std::mutex globalCallMutex_; 578 MarkedMessageQueue hostGlobalCallQueue_ {}; 579 MessageQueue workerGlobalCallQueue_ {}; 580 MessageQueue errorQueue_ {}; 581 582 uv_async_t* workerOnMessageSignal_ = nullptr; 583 uv_async_t* hostOnMessageSignal_ = nullptr; 584 uv_async_t* hostOnErrorSignal_ = nullptr; 585 uv_async_t* hostOnGlobalCallSignal_ = nullptr; 586 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 587 uv_async_t debuggerOnPostTaskSignal_ {}; 588 std::mutex debuggerMutex_; 589 std::queue<DebuggerPostTask> debuggerQueue_ {}; 590 #endif 591 592 std::atomic<RunnerState> runnerState_ {STARTING}; 593 std::atomic<HostState> hostState_ {ACTIVE}; 594 std::unique_ptr<WorkerRunner> runner_ {}; 595 596 std::atomic<bool> isErrorExit_ = false; 597 598 napi_env hostEnv_ {nullptr}; 599 napi_env workerEnv_ {nullptr}; 600 601 napi_ref workerRef_ {nullptr}; 602 napi_ref workerPort_ {nullptr}; 603 604 std::map<std::string, std::list<WorkerListener*>> eventListeners_ {}; 605 std::map<std::string, std::list<WorkerListener*>> parentPortEventListeners_ {}; 606 std::unordered_map<std::string, napi_ref> globalCallObjects_ {}; 607 std::queue<std::pair<int32_t, napi_value>> globalCallErrors_ {}; 608 std::atomic<uint32_t> globalCallId_ = 1; // 0: reserved for error check 609 610 std::recursive_mutex liveStatusLock_ {}; 611 std::mutex workerOnmessageMutex_ {}; 612 613 std::condition_variable cv_; 614 std::atomic<bool> globalCallSuccess_ = true; 615 std::function<void(napi_env)> workerEnvCallback_; 616 617 bool isMainThreadWorker_ = true; 618 bool isNewVersion_ = true; 619 std::atomic<bool> isTerminated_ = false; 620 std::atomic<bool> isHostEnvExited_ = false; 621 622 std::shared_ptr<WorkerWrapper> workerWrapper_ = nullptr; 623 624 friend class WorkersTest; 625 }; 626 } // namespace Commonlibrary::Concurrent::WorkerModule 627 #endif // JS_CONCURRENT_MODULE_WORKER_WORKER_H 628