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