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 HostEnvCleanCallback(void *data); 386 387 #if defined(ENABLE_WORKER_EVENTHANDLER) 388 static std::shared_ptr<OHOS::AppExecFwk::EventHandler> GetMainThreadHandler(); 389 #endif 390 391 void StartExecuteInThread(napi_env env, const char* script); 392 393 bool UpdateWorkerState(RunnerState state); 394 bool UpdateHostState(HostState state); 395 IsNotTerminate()396 bool IsNotTerminate() const 397 { 398 return runnerState_.load(std::memory_order_acquire) <= RUNNING; 399 } 400 IsRunning()401 bool IsRunning() const 402 { 403 return runnerState_.load(std::memory_order_acquire) == RUNNING; 404 } 405 IsTerminated()406 bool IsTerminated() const 407 { 408 return runnerState_.load(std::memory_order_acquire) >= TERMINATED; 409 } 410 IsTerminating()411 bool IsTerminating() const 412 { 413 return runnerState_.load(std::memory_order_acquire) == TERMINATEING; 414 } 415 SetScriptMode(ScriptMode mode)416 void SetScriptMode(ScriptMode mode) 417 { 418 scriptMode_ = mode; 419 } 420 421 void AddListenerInner(napi_env env, const char* type, const WorkerListener* listener); 422 void RemoveListenerInner(napi_env env, const char* type, napi_ref callback); 423 void RemoveAllListenerInner(); 424 void EraseWorker(); GetWorkerLoop()425 uv_loop_t* GetWorkerLoop() const 426 { 427 if (workerEnv_ != nullptr) { 428 return NapiHelper::GetLibUV(workerEnv_); 429 } 430 return nullptr; 431 } 432 SetWorkerEnv(napi_env workerEnv)433 void SetWorkerEnv(napi_env workerEnv) 434 { 435 workerEnv_ = workerEnv; 436 if (workerEnvCallback_) { 437 workerEnvCallback_(workerEnv_); 438 } 439 } 440 GetScript()441 std::string GetScript() const 442 { 443 return script_; 444 } 445 GetName()446 std::string GetName() const 447 { 448 return name_; 449 } 450 ClearWorkerTasks()451 bool ClearWorkerTasks() 452 { 453 if (hostEnv_ != nullptr) { 454 workerMessageQueue_.Clear(hostEnv_); 455 return true; 456 } 457 return false; 458 } 459 HostIsStop()460 bool HostIsStop() const 461 { 462 return hostState_.load(std::memory_order_acquire) == INACTIVE; 463 } 464 IsSameWorkerEnv(napi_env env)465 bool IsSameWorkerEnv(napi_env env) const 466 { 467 return workerEnv_ == env; 468 } 469 Loop()470 void Loop() const 471 { 472 uv_loop_t* loop = GetWorkerLoop(); 473 if (loop != nullptr) { 474 uv_run(loop, UV_RUN_DEFAULT); 475 } else { 476 return; 477 } 478 } 479 RegisterCallbackForWorkerEnv(std::function<void (napi_env)> callback)480 void RegisterCallbackForWorkerEnv(std::function<void (napi_env)> callback) 481 { 482 workerEnvCallback_ = callback; 483 if (workerEnv_ != nullptr) { 484 workerEnvCallback_(workerEnv_); 485 } 486 } 487 GetWorkerEnv()488 napi_env GetWorkerEnv() const 489 { 490 return workerEnv_; 491 } 492 GetHostEnv()493 napi_env GetHostEnv() const 494 { 495 return hostEnv_; 496 } 497 498 private: 499 void WorkerOnMessageInner(); 500 void HostOnMessageInner(); 501 void HostOnErrorInner(); 502 void HostOnMessageErrorInner(); 503 void HostOnGlobalCallInner(); 504 void WorkerOnMessageErrorInner(); 505 void WorkerOnErrorInner(napi_value error); 506 507 void HandleHostException() const; 508 void HandleException(); 509 void HandleUncaughtException(napi_value exception); 510 bool CallWorkerFunction(size_t argc, const napi_value* argv, const char* methodName, bool tryCatch); 511 void CallHostFunction(size_t argc, const napi_value* argv, const char* methodName) const; 512 513 bool HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type); 514 void ParentPortHandleEventListeners(napi_env env, napi_value recv, size_t argc, 515 const napi_value* argv, const char* type, bool tryCatch); 516 void TerminateInner(); 517 518 void PostMessageInner(MessageDataType data); 519 void PostMessageToHostInner(MessageDataType data); 520 521 void TerminateWorker(); 522 523 void CloseInner(); 524 525 void PublishWorkerOverSignal(); 526 void CloseWorkerCallback(); 527 void CloseHostCallback(); 528 529 void PostWorkerOverTask(); 530 void PostWorkerErrorTask(); 531 void PostWorkerMessageTask(); 532 void PostWorkerGlobalCallTask(); 533 static bool IsValidWorker(Worker* worker); 534 535 void InitHostHandle(uv_loop_t* loop); 536 void CloseHostHandle(); 537 538 void ReleaseWorkerThreadContent(); 539 void ReleaseHostThreadContent(); 540 bool PrepareForWorkerInstance(); 541 void ParentPortAddListenerInner(napi_env env, const char* type, const WorkerListener* listener); 542 void ParentPortRemoveAllListenerInner(); 543 void ParentPortRemoveListenerInner(napi_env env, const char* type, napi_ref callback); 544 void GetContainerScopeId(napi_env env); 545 546 void AddGlobalCallObject(const std::string &instanceName, napi_ref obj); 547 bool RemoveGlobalCallObject(const std::string &instanceName); 548 void ClearGlobalCallObject(); 549 void AddGlobalCallError(int32_t errCode, napi_value errData = nullptr); 550 void HandleGlobalCallError(napi_env env); 551 void ClearGlobalCallError(napi_env env); 552 void InitGlobalCallStatus(napi_env env); 553 void IncreaseGlobalCallId(); 554 555 void ClearHostMessage(napi_env env); 556 557 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 558 static void HandleDebuggerTask(const uv_async_t* req); 559 void DebuggerOnPostTask(std::function<void()>&& task); 560 #endif 561 562 std::string script_ {}; 563 std::string fileName_ {}; 564 std::string name_ {}; 565 ScriptMode scriptMode_ {CLASSIC}; 566 bool isLimitedWorker_ {false}; 567 bool isRelativePath_ {false}; 568 int32_t scopeId_ {-1}; 569 570 MessageQueue workerMessageQueue_ {}; 571 MessageQueue hostMessageQueue_ {}; 572 std::mutex globalCallMutex_; 573 MarkedMessageQueue hostGlobalCallQueue_ {}; 574 MessageQueue workerGlobalCallQueue_ {}; 575 MessageQueue errorQueue_ {}; 576 577 uv_async_t* workerOnMessageSignal_ = nullptr; 578 uv_async_t* hostOnMessageSignal_ = nullptr; 579 uv_async_t* hostOnErrorSignal_ = nullptr; 580 uv_async_t* hostOnGlobalCallSignal_ = nullptr; 581 #if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 582 uv_async_t debuggerOnPostTaskSignal_ {}; 583 std::mutex debuggerMutex_; 584 std::queue<DebuggerPostTask> debuggerQueue_ {}; 585 #endif 586 587 std::atomic<RunnerState> runnerState_ {STARTING}; 588 std::atomic<HostState> hostState_ {ACTIVE}; 589 std::unique_ptr<WorkerRunner> runner_ {}; 590 591 std::atomic<bool> isErrorExit_ = false; 592 593 napi_env hostEnv_ {nullptr}; 594 napi_env workerEnv_ {nullptr}; 595 596 napi_ref workerRef_ {nullptr}; 597 napi_ref workerPort_ {nullptr}; 598 599 std::map<std::string, std::list<WorkerListener*>> eventListeners_ {}; 600 std::map<std::string, std::list<WorkerListener*>> parentPortEventListeners_ {}; 601 std::unordered_map<std::string, napi_ref> globalCallObjects_ {}; 602 std::queue<std::pair<int32_t, napi_value>> globalCallErrors_ {}; 603 std::atomic<uint32_t> globalCallId_ = 1; // 0: reserved for error check 604 605 std::recursive_mutex liveStatusLock_ {}; 606 std::mutex workerOnmessageMutex_ {}; 607 608 std::condition_variable cv_; 609 std::atomic<bool> globalCallSuccess_ = true; 610 std::function<void(napi_env)> workerEnvCallback_; 611 612 bool isMainThreadWorker_ = true; 613 bool isNewVersion_ = true; 614 std::atomic<bool> isTerminated_ = false; 615 std::atomic<bool> isHostEnvExited_ = false; 616 617 std::shared_ptr<WorkerWrapper> workerWrapper_ = nullptr; 618 619 friend class WorkersTest; 620 }; 621 } // namespace Commonlibrary::Concurrent::WorkerModule 622 #endif // JS_CONCURRENT_MODULE_WORKER_WORKER_H 623