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#include "worker.h" 17 18#include "commonlibrary/ets_utils/js_sys_module/timer/timer.h" 19#include "helper/concurrent_helper.h" 20#include "helper/error_helper.h" 21#include "helper/hitrace_helper.h" 22#include "helper/path_helper.h" 23#include "tools/log.h" 24#if defined(OHOS_PLATFORM) 25#include "parameters.h" 26#endif 27 28namespace Commonlibrary::Concurrent::WorkerModule { 29using namespace OHOS::JsSysModule; 30static constexpr int8_t NUM_WORKER_ARGS = 2; 31static constexpr uint8_t NUM_GLOBAL_CALL_ARGS = 3; 32static std::list<Worker *> g_workers; 33static constexpr int MAX_WORKERS = 8; 34static constexpr int MAX_THREAD_WORKERS = 64; 35static std::mutex g_workersMutex; 36static std::list<Worker *> g_limitedworkers; 37static constexpr int MAX_LIMITEDWORKERS = 16; 38static std::mutex g_limitedworkersMutex; 39static constexpr uint8_t BEGIN_INDEX_OF_ARGUMENTS = 2; 40static constexpr uint32_t DEFAULT_TIMEOUT = 5000; 41static constexpr uint32_t GLOBAL_CALL_ID_MAX = 4294967295; 42static constexpr size_t GLOBAL_CALL_MAX_COUNT = 65535; 43 44#if defined(ENABLE_WORKER_EVENTHANDLER) 45std::shared_ptr<OHOS::AppExecFwk::EventHandler> Worker::GetMainThreadHandler() 46{ 47 static std::shared_ptr<OHOS::AppExecFwk::EventHandler> mainThreadHandler; 48 static std::mutex mainThreadHandlerMutex; 49 if (mainThreadHandler == nullptr) { 50 std::lock_guard<std::mutex> lock(mainThreadHandlerMutex); 51 if (mainThreadHandler == nullptr) { 52 mainThreadHandler = std::make_shared<OHOS::AppExecFwk::EventHandler>( 53 OHOS::AppExecFwk::EventRunner::GetMainEventRunner()); 54 } 55 } 56 return mainThreadHandler; 57} 58#endif 59 60Worker::Worker(napi_env env, napi_ref thisVar) 61 : hostEnv_(env), workerRef_(thisVar) 62{} 63 64napi_value Worker::InitWorker(napi_env env, napi_value exports) 65{ 66 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 67 napi_property_descriptor properties[] = { 68 DECLARE_NAPI_FUNCTION("postMessage", PostMessage), 69 DECLARE_NAPI_FUNCTION("postMessageWithSharedSendable", PostMessageWithSharedSendable), 70 DECLARE_NAPI_FUNCTION("terminate", Terminate), 71 DECLARE_NAPI_FUNCTION("on", On), 72 DECLARE_NAPI_FUNCTION("registerGlobalCallObject", RegisterGlobalCallObject), 73 DECLARE_NAPI_FUNCTION("unregisterGlobalCallObject", UnregisterGlobalCallObject), 74 DECLARE_NAPI_FUNCTION("once", Once), 75 DECLARE_NAPI_FUNCTION("off", Off), 76 DECLARE_NAPI_FUNCTION("addEventListener", AddEventListener), 77 DECLARE_NAPI_FUNCTION("dispatchEvent", DispatchEvent), 78 DECLARE_NAPI_FUNCTION("removeEventListener", RemoveEventListener), 79 DECLARE_NAPI_FUNCTION("removeAllListener", RemoveAllListener), 80 DECLARE_NAPI_FUNCTION("cancelTasks", CancelTask), 81 }; 82 // for worker.ThreadWorker 83 const char threadWorkerName[] = "ThreadWorker"; 84 napi_value threadWorkerClazz = nullptr; 85 napi_define_class(env, threadWorkerName, sizeof(threadWorkerName), Worker::ThreadWorkerConstructor, nullptr, 86 sizeof(properties) / sizeof(properties[0]), properties, &threadWorkerClazz); 87 napi_set_named_property(env, exports, "ThreadWorker", threadWorkerClazz); 88 89 // for worker.Worker 90 const char workerName[] = "Worker"; 91 napi_value workerClazz = nullptr; 92 napi_define_class(env, workerName, sizeof(workerName), Worker::WorkerConstructor, nullptr, 93 sizeof(properties) / sizeof(properties[0]), properties, &workerClazz); 94 napi_set_named_property(env, exports, "Worker", workerClazz); 95 96 // for worker.LimitedWorker 97 const char limitedWorkerName[] = "RestrictedWorker"; 98 napi_value limitedWorkerClazz = nullptr; 99 napi_define_class(env, limitedWorkerName, sizeof(limitedWorkerName), Worker::LimitedWorkerConstructor, nullptr, 100 sizeof(properties) / sizeof(properties[0]), properties, &limitedWorkerClazz); 101 napi_set_named_property(env, exports, "RestrictedWorker", limitedWorkerClazz); 102 return InitPort(env, exports); 103} 104 105napi_value Worker::InitPort(napi_env env, napi_value exports) 106{ 107 NativeEngine* engine = reinterpret_cast<NativeEngine*>(env); 108 Worker* worker = nullptr; 109 if (engine->IsRestrictedWorkerThread()) { 110 std::lock_guard<std::mutex> lock(g_limitedworkersMutex); 111 for (auto item = g_limitedworkers.begin(); item != g_limitedworkers.end(); item++) { 112 if ((*item)->IsSameWorkerEnv(env)) { 113 worker = *item; 114 } 115 } 116 } else if (engine->IsWorkerThread()) { 117 std::lock_guard<std::mutex> lock(g_workersMutex); 118 for (auto item = g_workers.begin(); item != g_workers.end(); item++) { 119 if ((*item)->IsSameWorkerEnv(env)) { 120 worker = *item; 121 } 122 } 123 } else { 124 return exports; 125 } 126 127 if (worker == nullptr) { 128 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is null when InitWorker"); 129 return exports; 130 } 131 132 napi_property_descriptor properties[] = { 133 DECLARE_NAPI_FUNCTION_WITH_DATA("postMessage", PostMessageToHost, worker), 134 DECLARE_NAPI_FUNCTION_WITH_DATA("postMessageWithSharedSendable", PostMessageWithSharedSendableToHost, worker), 135 DECLARE_NAPI_FUNCTION_WITH_DATA("callGlobalCallObjectMethod", GlobalCall, worker), 136 DECLARE_NAPI_FUNCTION_WITH_DATA("close", CloseWorker, worker), 137 DECLARE_NAPI_FUNCTION_WITH_DATA("cancelTasks", ParentPortCancelTask, worker), 138 DECLARE_NAPI_FUNCTION_WITH_DATA("addEventListener", ParentPortAddEventListener, worker), 139 DECLARE_NAPI_FUNCTION_WITH_DATA("dispatchEvent", ParentPortDispatchEvent, worker), 140 DECLARE_NAPI_FUNCTION_WITH_DATA("removeEventListener", ParentPortRemoveEventListener, worker), 141 DECLARE_NAPI_FUNCTION_WITH_DATA("removeAllListener", ParentPortRemoveAllListener, worker), 142 }; 143 napi_value workerPortObj = nullptr; 144 napi_create_object(env, &workerPortObj); 145 napi_define_properties(env, workerPortObj, sizeof(properties) / sizeof(properties[0]), properties); 146 147 // 5. register worker name in DedicatedWorkerGlobalScope 148 std::string name = worker->GetName(); 149 if (!name.empty()) { 150 napi_value nameValue = nullptr; 151 napi_create_string_utf8(env, name.c_str(), name.length(), &nameValue); 152 napi_set_named_property(env, workerPortObj, "name", nameValue); 153 } 154 155 napi_set_named_property(env, workerPortObj, "self", workerPortObj); 156 157 if (worker->isNewVersion_) { 158 napi_set_named_property(env, exports, "workerPort", workerPortObj); 159 } else { 160 napi_set_named_property(env, exports, "parentPort", workerPortObj); 161 } 162 // register worker Port. 163 napi_create_reference(env, workerPortObj, 1, &worker->workerPort_); 164#if defined(ENABLE_WORKER_EVENTHANDLER) 165 GetMainThreadHandler(); 166#endif 167 return exports; 168} 169 170napi_value Worker::LimitedWorkerConstructor(napi_env env, napi_callback_info cbinfo) 171{ 172 if (CanCreateWorker(env, WorkerVersion::NEW)) { 173 return Constructor(env, cbinfo, true); 174 } 175 HILOG_ERROR("worker:: using both Worker and LimitedWorker is not supported"); 176 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, 177 "Using both Worker and LimitedWorker is not supported."); 178 return nullptr; 179} 180 181napi_value Worker::ThreadWorkerConstructor(napi_env env, napi_callback_info cbinfo) 182{ 183 HITRACE_HELPER_METER_NAME("ThreadWorkerConstructor: [Add Thread]"); 184 if (CanCreateWorker(env, WorkerVersion::NEW)) { 185 return Constructor(env, cbinfo, false, WorkerVersion::NEW); 186 } 187 HILOG_ERROR("worker:: ThreadWorker construct failed"); 188 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, 189 "Using both Worker and ThreadWorker is not supported."); 190 return nullptr; 191} 192 193napi_value Worker::WorkerConstructor(napi_env env, napi_callback_info cbinfo) 194{ 195 HITRACE_HELPER_METER_NAME("WorkerConstructor: [Add Thread]"); 196 if (CanCreateWorker(env, WorkerVersion::OLD)) { 197 return Constructor(env, cbinfo, false, WorkerVersion::OLD); 198 } 199 HILOG_ERROR("worker:: using both Worker and other Workers is not supported"); 200 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, 201 "Using both Worker and other Workers is not supported."); 202 return nullptr; 203} 204 205napi_value Worker::Constructor(napi_env env, napi_callback_info cbinfo, bool limitSign, WorkerVersion version) 206{ 207 napi_value thisVar = nullptr; 208 void* data = nullptr; 209 size_t argc = 2; // 2: max args number is 2 210 napi_value args[argc]; 211 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); 212 // check argv count 213 if (argc < 1) { 214 HILOG_ERROR("worker:: the number of create worker param must be more than 1 with new"); 215 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the number of parameters must be more than 1."); 216 return nullptr; 217 } 218 // check 1st param is string 219 if (!NapiHelper::IsString(env, args[0])) { 220 HILOG_ERROR("worker:: the type of Worker 1st param must be string"); 221 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 222 "the type of the first param must be string."); 223 return nullptr; 224 } 225 WorkerParams* workerParams = nullptr; 226 if (argc == 2) { // 2: max args number is 2 227 workerParams = CheckWorkerArgs(env, args[1]); 228 if (workerParams == nullptr) { 229 HILOG_ERROR("Worker:: arguments check failed."); 230 return nullptr; 231 } 232 } 233 Worker* worker = nullptr; 234 if (limitSign) { 235 std::lock_guard<std::mutex> lock(g_limitedworkersMutex); 236 if (static_cast<int>(g_limitedworkers.size()) >= MAX_LIMITEDWORKERS) { 237 HILOG_ERROR("worker:: the number of limiteworkers exceeds the maximum"); 238 ErrorHelper::ThrowError(env, 239 ErrorHelper::ERR_WORKER_INITIALIZATION, "the number of limiteworkers exceeds the maximum."); 240 return nullptr; 241 } 242 243 // 2. new worker instance 244 worker = new Worker(env, nullptr); 245 if (worker == nullptr) { 246 HILOG_ERROR("worker:: create worker error"); 247 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, "create worker error"); 248 return nullptr; 249 } 250 g_limitedworkers.push_back(worker); 251 HILOG_INFO("worker:: limited workers num %{public}zu", g_limitedworkers.size()); 252 } else { 253 int maxWorkers = (version == WorkerVersion::NEW) ? MAX_THREAD_WORKERS : MAX_WORKERS; 254 #if defined(OHOS_PLATFORM) 255 maxWorkers = OHOS::system::GetIntParameter<int>("persist.commonlibrary.maxworkers", maxWorkers); 256 #endif 257 std::lock_guard<std::mutex> lock(g_workersMutex); 258 if (static_cast<int>(g_workers.size()) >= maxWorkers) { 259 HILOG_ERROR("worker:: the number of workers exceeds the maximum"); 260 ErrorHelper::ThrowError(env, 261 ErrorHelper::ERR_WORKER_INITIALIZATION, "the number of workers exceeds the maximum."); 262 return nullptr; 263 } 264 265 // 2. new worker instance 266 worker = new Worker(env, nullptr); 267 if (worker == nullptr) { 268 HILOG_ERROR("worker:: create worker error"); 269 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, "create worker error"); 270 return nullptr; 271 } 272 g_workers.push_back(worker); 273 HILOG_INFO("worker:: workers num %{public}zu", g_workers.size()); 274 } 275 276 if (workerParams != nullptr) { 277 if (!workerParams->name_.empty()) { 278 worker->name_ = workerParams->name_; 279 } 280 // default classic 281 worker->SetScriptMode(workerParams->type_); 282 CloseHelp::DeletePointer(workerParams, false); 283 workerParams = nullptr; 284 } 285 worker->isLimitedWorker_ = limitSign; 286 worker->isNewVersion_ = (version != WorkerVersion::OLD) ? true : false; 287 288 // 3. execute in thread 289 char* script = NapiHelper::GetChars(env, args[0]); 290 if (script == nullptr) { 291 HILOG_ERROR("worker:: the file path is invaild, maybe path is null"); 292 ErrorHelper::ThrowError(env, 293 ErrorHelper::ERR_WORKER_INVALID_FILEPATH, "the file path is invaild, maybe path is null."); 294 return nullptr; 295 } 296 napi_add_env_cleanup_hook(env, HostEnvCleanCallback, worker); 297 napi_wrap(env, thisVar, worker, WorkerDestructor, nullptr, &worker->workerRef_); 298 worker->StartExecuteInThread(env, script); 299 return thisVar; 300} 301 302void Worker::WorkerDestructor(napi_env env, void *data, void *hint) 303{ 304 Worker* worker = reinterpret_cast<Worker*>(data); 305 napi_remove_env_cleanup_hook(env, HostEnvCleanCallback, worker); 306 std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_); 307 if (worker->isHostEnvExited_) { 308 HILOG_INFO("worker:: host env exit."); 309 return; 310 } 311 if (worker->UpdateHostState(INACTIVE)) { 312#if defined(ENABLE_WORKER_EVENTHANDLER) 313 if (!worker->isMainThreadWorker_ || worker->isLimitedWorker_) { 314 worker->CloseHostHandle(); 315 } 316#else 317 worker->CloseHostHandle(); 318#endif 319 worker->ReleaseHostThreadContent(); 320 } 321 if (!worker->IsRunning()) { 322 HILOG_DEBUG("worker:: worker is not running"); 323 return; 324 } 325 worker->TerminateInner(); 326} 327 328void Worker::HostEnvCleanCallback(void *data) 329{ 330 Worker* worker = reinterpret_cast<Worker*>(data); 331 if (worker == nullptr) { 332 HILOG_INFO("worker:: worker is nullptr when host env exit."); 333 return; 334 } 335 if (!IsValidWorker(worker)) { 336 HILOG_INFO("worker:: worker is terminated when host env exit."); 337 return; 338 } 339 std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_); 340 worker->isHostEnvExited_ = true; 341#if defined(ENABLE_WORKER_EVENTHANDLER) 342 if (!worker->isMainThreadWorker_ || worker->isLimitedWorker_) { 343 worker->CloseHostHandle(); 344 } 345#else 346 worker->CloseHostHandle(); 347#endif 348 worker->ReleaseHostThreadContent(); 349 worker->RemoveAllListenerInner(); 350 worker->ClearGlobalCallObject(); 351} 352 353Worker::WorkerParams* Worker::CheckWorkerArgs(napi_env env, napi_value argsValue) 354{ 355 WorkerParams* workerParams = nullptr; 356 if (NapiHelper::IsObject(env, argsValue)) { 357 workerParams = new WorkerParams(); 358 napi_value nameValue = NapiHelper::GetNameProperty(env, argsValue, "name"); 359 if (NapiHelper::IsNotUndefined(env, nameValue)) { 360 if (!NapiHelper::IsString(env, nameValue)) { 361 CloseHelp::DeletePointer(workerParams, false); 362 WorkerThrowError(env, ErrorHelper::TYPE_ERROR, "the type of name must be string."); 363 return nullptr; 364 } 365 char* nameStr = NapiHelper::GetChars(env, nameValue); 366 if (nameStr == nullptr) { 367 CloseHelp::DeletePointer(workerParams, false); 368 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, "the name of worker is null."); 369 return nullptr; 370 } 371 workerParams->name_ = std::string(nameStr); 372 CloseHelp::DeletePointer(nameStr, true); 373 } 374 napi_value typeValue = NapiHelper::GetNameProperty(env, argsValue, "type"); 375 if (NapiHelper::IsNotUndefined(env, typeValue)) { 376 if (!NapiHelper::IsString(env, typeValue)) { 377 CloseHelp::DeletePointer(workerParams, false); 378 WorkerThrowError(env, ErrorHelper::TYPE_ERROR, 379 "the type of type's value must be string."); 380 return nullptr; 381 } 382 char* typeStr = NapiHelper::GetChars(env, typeValue); 383 if (typeStr == nullptr) { 384 CloseHelp::DeletePointer(workerParams, false); 385 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INITIALIZATION, "the type of worker is null."); 386 return nullptr; 387 } 388 if (strcmp("classic", typeStr) == 0) { 389 workerParams->type_ = CLASSIC; 390 CloseHelp::DeletePointer(typeStr, true); 391 } else { 392 CloseHelp::DeletePointer(workerParams, false); 393 CloseHelp::DeletePointer(typeStr, true); 394 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 395 "the type must be classic, unsupport others now."); 396 return nullptr; 397 } 398 } 399 } 400 return workerParams; 401} 402 403napi_value Worker::PostMessage(napi_env env, napi_callback_info cbinfo) 404{ 405 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 406 return CommonPostMessage(env, cbinfo, true); 407} 408 409napi_value Worker::PostMessageWithSharedSendable(napi_env env, napi_callback_info cbinfo) 410{ 411 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 412 return CommonPostMessage(env, cbinfo, false); 413} 414 415napi_value Worker::CommonPostMessage(napi_env env, napi_callback_info cbinfo, bool cloneSendable) 416{ 417 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 418 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); 419 if (argc < 1) { 420 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 421 "Worker messageObject must be not null with postMessage"); 422 return nullptr; 423 } 424 napi_value* argv = new napi_value[argc]; 425 ObjectScope<napi_value> scope(argv, true); 426 napi_value thisVar = nullptr; 427 napi_get_cb_info(env, cbinfo, &argc, argv, &thisVar, nullptr); 428 Worker* worker = nullptr; 429 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker)); 430 431 if (worker == nullptr || worker->IsTerminated() || worker->IsTerminating()) { 432 HILOG_ERROR("worker:: worker is nullptr when PostMessage, maybe worker is terminated"); 433 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated when PostMessage"); 434 return nullptr; 435 } 436 437 MessageDataType data = nullptr; 438 napi_status serializeStatus = napi_ok; 439 bool defaultClone = cloneSendable ? true : false; 440 napi_value undefined = NapiHelper::GetUndefinedValue(env); 441 if (argc >= NUM_WORKER_ARGS) { 442 if (!NapiHelper::IsArray(env, argv[1])) { 443 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 444 "the type of the transfer list must be an array."); 445 return nullptr; 446 } 447 serializeStatus = napi_serialize_inner(env, argv[0], argv[1], undefined, false, defaultClone, &data); 448 } else { 449 serializeStatus = napi_serialize_inner(env, argv[0], undefined, undefined, false, defaultClone, &data); 450 } 451 if (serializeStatus != napi_ok || data == nullptr) { 452 worker->HostOnMessageErrorInner(); 453 WorkerThrowError(env, ErrorHelper::ERR_WORKER_SERIALIZATION, "failed to serialize message."); 454 return nullptr; 455 } 456 worker->PostMessageInner(data); 457 return NapiHelper::GetUndefinedValue(env); 458} 459 460napi_value Worker::Terminate(napi_env env, napi_callback_info cbinfo) 461{ 462 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 463 napi_value thisVar = nullptr; 464 napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr); 465 Worker* worker = nullptr; 466 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker)); 467 if (worker == nullptr) { 468 HILOG_ERROR("worker:: worker is nullptr when Terminate, maybe worker is terminated"); 469 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is nullptr when Terminate"); 470 return nullptr; 471 } 472 bool expected = false; 473 if (worker->isTerminated_.compare_exchange_weak(expected, true)) { 474 HILOG_DEBUG("worker:: Terminate worker"); 475 } else { 476 HILOG_DEBUG("worker:: worker is terminated when Terminate"); 477 return nullptr; 478 } 479 if (worker->IsTerminated() || worker->IsTerminating()) { 480 HILOG_DEBUG("worker:: worker is not in running when Terminate"); 481 return nullptr; 482 } 483 worker->TerminateInner(); 484 return NapiHelper::GetUndefinedValue(env); 485} 486 487napi_value Worker::On(napi_env env, napi_callback_info cbinfo) 488{ 489 return AddListener(env, cbinfo, PERMANENT); 490} 491 492napi_value Worker::Once(napi_env env, napi_callback_info cbinfo) 493{ 494 return AddListener(env, cbinfo, ONCE); 495} 496 497napi_value Worker::RegisterGlobalCallObject(napi_env env, napi_callback_info cbinfo) 498{ 499 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); 500 if (argc != NUM_WORKER_ARGS) { 501 ErrorHelper::ThrowError(env, 502 ErrorHelper::TYPE_ERROR, "the number of parameters must be 2."); 503 return nullptr; 504 } 505 // check 1st param is string 506 napi_value thisVar = nullptr; 507 void* data = nullptr; 508 napi_value* args = new napi_value[argc]; 509 ObjectScope<napi_value> scope(args, true); 510 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); 511 if (!NapiHelper::IsString(env, args[0])) { 512 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 513 "the type of instanceName must be string."); 514 return nullptr; 515 } 516 std::string instanceName = NapiHelper::GetString(env, args[0]); 517 518 Worker* worker = nullptr; 519 napi_unwrap(env, thisVar, (void**)&worker); 520 if (worker == nullptr) { 521 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated"); 522 return nullptr; 523 } 524 napi_ref obj = NapiHelper::CreateReference(env, args[1], 1); 525 worker->AddGlobalCallObject(instanceName, obj); 526 return nullptr; 527} 528 529napi_value Worker::UnregisterGlobalCallObject(napi_env env, napi_callback_info cbinfo) 530{ 531 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); 532 if (argc > 1) { 533 ErrorHelper::ThrowError(env, 534 ErrorHelper::TYPE_ERROR, "the number of the parameters must be 1 or 0."); 535 return nullptr; 536 } 537 napi_value thisVar = nullptr; 538 void* data = nullptr; 539 napi_value* args = new napi_value[argc]; 540 ObjectScope<napi_value> scope(args, true); 541 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); 542 Worker* worker = nullptr; 543 napi_unwrap(env, thisVar, (void**)&worker); 544 if (worker == nullptr) { 545 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated"); 546 return nullptr; 547 } 548 if (argc == 0) { 549 worker->ClearGlobalCallObject(); 550 HILOG_DEBUG("worker:: clear all registered globalCallObject"); 551 return nullptr; 552 } 553 // check 1st param is string 554 if (!NapiHelper::IsString(env, args[0])) { 555 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 556 "the type of instanceName must be string."); 557 return nullptr; 558 } 559 std::string instanceName = NapiHelper::GetString(env, args[0]); 560 if (!worker->RemoveGlobalCallObject(instanceName)) { 561 HILOG_ERROR("worker:: unregister unexist globalCallObject"); 562 } 563 return nullptr; 564} 565 566napi_value Worker::Off(napi_env env, napi_callback_info cbinfo) 567{ 568 return RemoveListener(env, cbinfo); 569} 570 571napi_value Worker::RemoveEventListener(napi_env env, napi_callback_info cbinfo) 572{ 573 return RemoveListener(env, cbinfo); 574} 575 576napi_value Worker::AddEventListener(napi_env env, napi_callback_info cbinfo) 577{ 578 return AddListener(env, cbinfo, PERMANENT); 579} 580 581napi_value Worker::AddListener(napi_env env, napi_callback_info cbinfo, ListenerMode mode) 582{ 583 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); 584 if (argc < NUM_WORKER_ARGS) { 585 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 586 "the number of listener parameters is not less than 2."); 587 return nullptr; 588 } 589 // check 1st param is string 590 napi_value thisVar = nullptr; 591 void* data = nullptr; 592 napi_value* args = new napi_value[argc]; 593 ObjectScope<napi_value> scope(args, true); 594 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); 595 if (!NapiHelper::IsString(env, args[0])) { 596 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 597 "the type of listener first param must be string."); 598 return nullptr; 599 } 600 if (!NapiHelper::IsCallable(env, args[1])) { 601 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 602 "the type of listener the second param must be callable."); 603 return nullptr; 604 } 605 Worker* worker = nullptr; 606 napi_unwrap(env, thisVar, (void**)&worker); 607 if (worker == nullptr) { 608 HILOG_ERROR("worker:: worker is nullptr when addListener, maybe worker is terminated"); 609 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated"); 610 return nullptr; 611 } 612 613 napi_ref callback = NapiHelper::CreateReference(env, args[1], 1); 614 auto listener = new WorkerListener(env, callback, mode); 615 if (mode == ONCE && argc > NUM_WORKER_ARGS) { 616 if (NapiHelper::IsObject(env, args[NUM_WORKER_ARGS])) { 617 napi_value onceValue = NapiHelper::GetNameProperty(env, args[NUM_WORKER_ARGS], "once"); 618 bool isOnce = NapiHelper::GetBooleanValue(env, onceValue); 619 if (!isOnce) { 620 listener->SetMode(PERMANENT); 621 } 622 } 623 } 624 char* typeStr = NapiHelper::GetChars(env, args[0]); 625 worker->AddListenerInner(env, typeStr, listener); 626 CloseHelp::DeletePointer(typeStr, true); 627 return NapiHelper::GetUndefinedValue(env); 628} 629 630napi_value Worker::RemoveListener(napi_env env, napi_callback_info cbinfo) 631{ 632 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); 633 if (argc < 1) { 634 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 635 "the number of parameters is not less than 1."); 636 return nullptr; 637 } 638 // check 1st param is string 639 napi_value thisVar = nullptr; 640 void* data = nullptr; 641 napi_value* args = new napi_value[argc]; 642 ObjectScope<napi_value> scope(args, true); 643 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); 644 if (!NapiHelper::IsString(env, args[0])) { 645 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 646 "the type of removelistener the first param must be string."); 647 return nullptr; 648 } 649 650 Worker* worker = nullptr; 651 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker)); 652 if (worker == nullptr) { 653 HILOG_ERROR("worker:: worker is nullptr when RemoveListener, maybe worker is terminated"); 654 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated"); 655 return nullptr; 656 } 657 658 if (argc > 1 && !NapiHelper::IsCallable(env, args[1])) { 659 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 660 "the type of removelistener the second param must be callable."); 661 return nullptr; 662 } 663 664 char* typeStr = NapiHelper::GetChars(env, args[0]); 665 if (typeStr == nullptr) { 666 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the type of remove listener type must be not null"); 667 return nullptr; 668 } 669 670 napi_ref callback = nullptr; 671 if (argc > 1 && NapiHelper::IsCallable(env, args[1])) { 672 napi_create_reference(env, args[1], 1, &callback); 673 } 674 worker->RemoveListenerInner(env, typeStr, callback); 675 CloseHelp::DeletePointer(typeStr, true); 676 NapiHelper::DeleteReference(env, callback); 677 return NapiHelper::GetUndefinedValue(env); 678} 679 680void CallWorkCallback(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type) 681{ 682 napi_value callback = nullptr; 683 napi_get_named_property(env, recv, type, &callback); 684 if (NapiHelper::IsCallable(env, callback)) { 685 napi_value callbackResult = nullptr; 686 napi_call_function(env, recv, callback, argc, argv, &callbackResult); 687 } 688} 689 690napi_value Worker::DispatchEvent(napi_env env, napi_callback_info cbinfo) 691{ 692 size_t argc = 1; 693 napi_value args[1]; 694 napi_value thisVar = nullptr; 695 void* data = nullptr; 696 napi_get_cb_info(env, cbinfo, &argc, args, &thisVar, &data); 697 if (argc < 1) { 698 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the number of the parameters must be more than 1."); 699 return NapiHelper::CreateBooleanValue(env, false); 700 } 701 702 // check 1st param is event 703 if (!NapiHelper::IsObject(env, args[0])) { 704 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 705 "the type of DispatchEvent first param must be event object."); 706 return NapiHelper::CreateBooleanValue(env, false); 707 } 708 709 Worker* worker = nullptr; 710 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker)); 711 if (worker == nullptr) { 712 HILOG_ERROR("worker:: worker is nullptr when DispatchEvent, maybe worker is terminated"); 713 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker has been terminated"); 714 return NapiHelper::CreateBooleanValue(env, false); 715 } 716 717 napi_value typeValue = NapiHelper::GetNameProperty(env, args[0], "type"); 718 if (!NapiHelper::IsString(env, typeValue)) { 719 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 720 "the type of event type must be string."); 721 return NapiHelper::CreateBooleanValue(env, false); 722 } 723 724 napi_value obj = NapiHelper::GetReferenceValue(env, worker->workerRef_); 725 726 char* typeStr = NapiHelper::GetChars(env, typeValue); 727 if (typeStr == nullptr) { 728 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "dispatchEvent event type must be not null"); 729 return NapiHelper::CreateBooleanValue(env, false); 730 } 731 if (strcmp(typeStr, "error") == 0) { 732 CallWorkCallback(env, obj, 1, args, "onerror"); 733 } else if (strcmp(typeStr, "messageerror") == 0) { 734 CallWorkCallback(env, obj, 1, args, "onmessageerror"); 735 } else if (strcmp(typeStr, "message") == 0) { 736 CallWorkCallback(env, obj, 1, args, "onmessage"); 737 } 738 739 worker->HandleEventListeners(env, obj, 1, args, typeStr); 740 741 CloseHelp::DeletePointer(typeStr, true); 742 return NapiHelper::CreateBooleanValue(env, true); 743} 744 745napi_value Worker::RemoveAllListener(napi_env env, napi_callback_info cbinfo) 746{ 747 napi_value thisVar = nullptr; 748 napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr); 749 Worker* worker = nullptr; 750 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker)); 751 if (worker == nullptr) { 752 HILOG_ERROR("worker:: worker is nullptr when RemoveAllListener, maybe worker is terminated"); 753 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "maybe worker is terminated"); 754 return nullptr; 755 } 756 757 worker->RemoveAllListenerInner(); 758 return NapiHelper::GetUndefinedValue(env); 759} 760 761napi_value Worker::CancelTask(napi_env env, napi_callback_info cbinfo) 762{ 763 napi_value thisVar = nullptr; 764 napi_get_cb_info(env, cbinfo, nullptr, nullptr, &thisVar, nullptr); 765 Worker* worker = nullptr; 766 napi_unwrap(env, thisVar, reinterpret_cast<void**>(&worker)); 767 if (worker == nullptr) { 768 HILOG_ERROR("worker:: worker is nullptr when CancelTask, maybe worker is terminated"); 769 return nullptr; 770 } 771 772 if (worker->IsTerminated() || worker->IsTerminating()) { 773 HILOG_INFO("worker:: worker is not in running"); 774 return nullptr; 775 } 776 777 if (!worker->ClearWorkerTasks()) { 778 HILOG_ERROR("worker:: clear worker task error"); 779 } 780 return NapiHelper::GetUndefinedValue(env); 781} 782 783napi_value Worker::PostMessageToHost(napi_env env, napi_callback_info cbinfo) 784{ 785 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 786 return CommonPostMessageToHost(env, cbinfo, true); 787} 788 789napi_value Worker::PostMessageWithSharedSendableToHost(napi_env env, napi_callback_info cbinfo) 790{ 791 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 792 return CommonPostMessageToHost(env, cbinfo, false); 793} 794 795napi_value Worker::CommonPostMessageToHost(napi_env env, napi_callback_info cbinfo, bool cloneSendable) 796{ 797 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 798 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); 799 if (argc < 1) { 800 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the number of parameters must be more than 1."); 801 return nullptr; 802 } 803 napi_value* argv = new napi_value[argc]; 804 ObjectScope<napi_value> scope(argv, true); 805 Worker* worker = nullptr; 806 napi_get_cb_info(env, cbinfo, &argc, argv, nullptr, reinterpret_cast<void**>(&worker)); 807 808 if (worker == nullptr) { 809 HILOG_ERROR("worker:: when post message to host occur worker is nullptr"); 810 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is nullptr when post message to host"); 811 return nullptr; 812 } 813 814 if (!worker->IsRunning()) { 815 // if worker is not running, don't send any message to host thread 816 HILOG_DEBUG("worker:: when post message to host occur worker is not in running."); 817 return nullptr; 818 } 819 820 MessageDataType data = nullptr; 821 napi_status serializeStatus = napi_ok; 822 bool defaultClone = cloneSendable ? true : false; 823 napi_value undefined = NapiHelper::GetUndefinedValue(env); 824 if (argc >= NUM_WORKER_ARGS) { 825 if (!NapiHelper::IsArray(env, argv[1])) { 826 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "Transfer list must be an Array"); 827 return nullptr; 828 } 829 serializeStatus = napi_serialize_inner(env, argv[0], argv[1], undefined, false, defaultClone, &data); 830 } else { 831 napi_value undefined = NapiHelper::GetUndefinedValue(env); 832 serializeStatus = napi_serialize_inner(env, argv[0], undefined, undefined, false, defaultClone, &data); 833 } 834 835 if (serializeStatus != napi_ok || data == nullptr) { 836 worker->WorkerOnMessageErrorInner(); 837 WorkerThrowError(env, ErrorHelper::ERR_WORKER_SERIALIZATION, "failed to serialize message."); 838 return nullptr; 839 } 840 worker->PostMessageToHostInner(data); 841 return NapiHelper::GetUndefinedValue(env); 842} 843 844napi_value Worker::GlobalCall(napi_env env, napi_callback_info cbinfo) 845{ 846 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 847 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); 848 if (argc < NUM_GLOBAL_CALL_ARGS) { 849 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 850 "the number of parameters must be equal or more than 3."); 851 return nullptr; 852 } 853 napi_value* args = new napi_value[argc]; 854 ObjectScope<napi_value> scope(args, true); 855 Worker* worker = nullptr; 856 napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker)); 857 if (worker == nullptr) { 858 HILOG_ERROR("worker:: worker is null when callGlobalCallObjectMethod to host"); 859 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, 860 "worker is null when callGlobalCallObjectMethod to host"); 861 return nullptr; 862 } 863 864 if (!worker->IsRunning()) { 865 // if worker is not running, don't send any message to host thread 866 HILOG_DEBUG("worker:: when post message to host occur worker is not in running."); 867 return nullptr; 868 } 869 870 if (!NapiHelper::IsString(env, args[0])) { 871 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 872 "the type of instanceName must be string."); 873 return nullptr; 874 } 875 if (!NapiHelper::IsString(env, args[1])) { 876 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 877 "the type of methodname must be string."); 878 return nullptr; 879 } 880 if (!NapiHelper::IsNumber(env, args[2])) { // 2: the index of argument "timeout" 881 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 882 "the type of timeout must be number."); 883 return nullptr; 884 } 885 886 napi_status serializeStatus = napi_ok; 887 MessageDataType data = nullptr; 888 napi_value argsArray; 889 napi_create_array_with_length(env, argc - 1, &argsArray); 890 size_t index = 0; 891 uint32_t timeout = 0; 892 for (size_t i = 0; i < argc; i++) { 893 if (i == 2) { // 2: index of time limitation arg 894 timeout = NapiHelper::GetUint32Value(env, args[i]); 895 continue; 896 } 897 napi_set_element(env, argsArray, index, args[i]); 898 index++; 899 } 900 if (timeout <= 0 || timeout > DEFAULT_TIMEOUT) { 901 timeout = DEFAULT_TIMEOUT; 902 } 903 904 // defautly not transfer 905 napi_value undefined = NapiHelper::GetUndefinedValue(env); 906 // meaningless to copy sendable object when call globalObject 907 bool defaultClone = true; 908 bool defaultTransfer = false; 909 serializeStatus = napi_serialize_inner(env, argsArray, undefined, undefined, defaultTransfer, defaultClone, &data); 910 if (serializeStatus != napi_ok || data == nullptr) { 911 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_SERIALIZATION, "failed to serialize message."); 912 return nullptr; 913 } 914 worker->hostGlobalCallQueue_.Push(worker->globalCallId_, data); 915 916 std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_); 917 if (env != nullptr && !worker->HostIsStop() && !worker->isHostEnvExited_) { 918 worker->InitGlobalCallStatus(env); 919#if defined(ENABLE_WORKER_EVENTHANDLER) 920 if (worker->isMainThreadWorker_ && !worker->isLimitedWorker_) { 921 worker->PostWorkerGlobalCallTask(); 922 } else { 923 uv_async_send(worker->hostOnGlobalCallSignal_); 924 } 925#else 926 uv_async_send(worker->hostOnGlobalCallSignal_); 927#endif 928 } else { 929 HILOG_ERROR("worker:: worker host engine is nullptr when callGloballCallObjectMethod."); 930 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is null"); 931 return nullptr; 932 } 933 934 { 935 std::unique_lock lock(worker->globalCallMutex_); 936 if (!worker->cv_.wait_for(lock, std::chrono::milliseconds(timeout), [worker]() { 937 return !worker->workerGlobalCallQueue_.IsEmpty() || !worker->globalCallSuccess_; 938 })) { 939 worker->IncreaseGlobalCallId(); 940 HILOG_ERROR("worker:: callGlobalCallObjectMethod has exceeded the waiting time limitation, skip this turn"); 941 ErrorHelper::ThrowError(env, ErrorHelper::ERR_GLOBAL_CALL_TIMEOUT); 942 return nullptr; 943 } 944 } 945 worker->IncreaseGlobalCallId(); 946 if (!worker->globalCallSuccess_) { 947 worker->HandleGlobalCallError(env); 948 return nullptr; 949 } 950 if (!worker->workerGlobalCallQueue_.DeQueue(&data)) { 951 HILOG_ERROR("worker:: message returned from host is empty when callGloballCallObjectMethod"); 952 return nullptr; 953 } 954 napi_value res = nullptr; 955 serializeStatus = napi_deserialize(env, data, &res); 956 napi_delete_serialization_data(env, data); 957 if (serializeStatus != napi_ok || res == nullptr) { 958 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_SERIALIZATION, "failed to serialize message."); 959 return nullptr; 960 } 961 return res; 962} 963 964void Worker::InitGlobalCallStatus(napi_env env) 965{ 966 // worker side event data queue shall be empty before uv_async_send 967 workerGlobalCallQueue_.Clear(env); 968 ClearGlobalCallError(env); 969 globalCallSuccess_ = true; 970} 971 972void Worker::IncreaseGlobalCallId() 973{ 974 if (UNLIKELY(globalCallId_ == GLOBAL_CALL_ID_MAX)) { 975 globalCallId_ = 1; 976 } else { 977 globalCallId_++; 978 } 979} 980 981napi_value Worker::CloseWorker(napi_env env, napi_callback_info cbinfo) 982{ 983 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 984 Worker* worker = nullptr; 985 napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, (void**)&worker); 986 if (worker != nullptr) { 987 worker->CloseInner(); 988 } else { 989 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is null"); 990 return nullptr; 991 } 992 return NapiHelper::GetUndefinedValue(env); 993} 994 995napi_value Worker::ParentPortCancelTask(napi_env env, napi_callback_info cbinfo) 996{ 997 Worker* worker = nullptr; 998 napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, reinterpret_cast<void**>(&worker)); 999 if (worker == nullptr) { 1000 HILOG_ERROR("worker:: worker is nullptr when CancelTask, maybe worker is terminated"); 1001 return nullptr; 1002 } 1003 1004 if (worker->IsTerminated() || worker->IsTerminating()) { 1005 HILOG_INFO("worker:: worker is not in running"); 1006 return nullptr; 1007 } 1008 1009 if (!worker->ClearWorkerTasks()) { 1010 HILOG_ERROR("worker:: clear worker task error"); 1011 } 1012 return NapiHelper::GetUndefinedValue(env); 1013} 1014 1015napi_value Worker::ParentPortAddEventListener(napi_env env, napi_callback_info cbinfo) 1016{ 1017 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); 1018 if (argc < NUM_WORKER_ARGS) { 1019 ErrorHelper::ThrowError(env, 1020 ErrorHelper::TYPE_ERROR, "worker listener param count must be more than WORKPARAMNUM."); 1021 return nullptr; 1022 } 1023 1024 napi_value* args = new napi_value[argc]; 1025 ObjectScope<napi_value> scope(args, true); 1026 Worker* worker = nullptr; 1027 napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker)); 1028 1029 if (!NapiHelper::IsString(env, args[0])) { 1030 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 1031 "the type of worker listener first param must be string."); 1032 return nullptr; 1033 } 1034 1035 if (!NapiHelper::IsCallable(env, args[1])) { 1036 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 1037 "the type of worker listener second param must be callable."); 1038 return nullptr; 1039 } 1040 1041 if (worker == nullptr || !worker->IsNotTerminate()) { 1042 HILOG_ERROR("worker:: when post message to host occur worker is nullptr"); 1043 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is not running."); 1044 return nullptr; 1045 } 1046 1047 napi_ref callback = NapiHelper::CreateReference(env, args[1], 1); 1048 auto listener = new WorkerListener(env, callback, PERMANENT); 1049 if (argc > NUM_WORKER_ARGS && NapiHelper::IsObject(env, args[NUM_WORKER_ARGS])) { 1050 napi_value onceValue = NapiHelper::GetNameProperty(env, args[NUM_WORKER_ARGS], "once"); 1051 bool isOnce = NapiHelper::GetBooleanValue(env, onceValue); 1052 if (isOnce) { 1053 listener->SetMode(ONCE); 1054 } 1055 } 1056 char* typeStr = NapiHelper::GetChars(env, args[0]); 1057 worker->ParentPortAddListenerInner(env, typeStr, listener); 1058 CloseHelp::DeletePointer(typeStr, true); 1059 return NapiHelper::GetUndefinedValue(env); 1060} 1061 1062napi_value Worker::ParentPortDispatchEvent(napi_env env, napi_callback_info cbinfo) 1063{ 1064 size_t argc = 1; 1065 napi_value args[1]; 1066 Worker* worker = nullptr; 1067 napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker)); 1068 if (argc < 1) { 1069 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "DispatchEvent param count must be more than 1."); 1070 return NapiHelper::CreateBooleanValue(env, false); 1071 } 1072 1073 if (!NapiHelper::IsObject(env, args[0])) { 1074 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 1075 "the type of worker DispatchEvent first param must be Event."); 1076 return NapiHelper::CreateBooleanValue(env, false); 1077 } 1078 1079 napi_value typeValue = NapiHelper::GetNameProperty(env, args[0], "type"); 1080 if (!NapiHelper::IsString(env, typeValue)) { 1081 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 1082 "the type of worker event must be string."); 1083 return NapiHelper::CreateBooleanValue(env, false); 1084 } 1085 1086 if (worker == nullptr || !worker->IsNotTerminate()) { 1087 HILOG_ERROR("worker:: when post message to host occur worker is nullptr"); 1088 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is nullptr."); 1089 return NapiHelper::CreateBooleanValue(env, false); 1090 } 1091 1092 char* typeStr = NapiHelper::GetChars(env, typeValue); 1093 if (typeStr == nullptr) { 1094 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "worker listener type must be not null."); 1095 return NapiHelper::CreateBooleanValue(env, false); 1096 } 1097 1098 napi_value obj = NapiHelper::GetReferenceValue(env, worker->workerPort_); 1099 1100 if (strcmp(typeStr, "error") == 0) { 1101 CallWorkCallback(env, obj, 1, args, "onerror"); 1102 } else if (strcmp(typeStr, "messageerror") == 0) { 1103 CallWorkCallback(env, obj, 1, args, "onmessageerror"); 1104 } else if (strcmp(typeStr, "message") == 0) { 1105 CallWorkCallback(env, obj, 1, args, "onmessage"); 1106 } 1107 1108 worker->ParentPortHandleEventListeners(env, obj, 1, args, typeStr, true); 1109 1110 CloseHelp::DeletePointer(typeStr, true); 1111 return NapiHelper::CreateBooleanValue(env, true); 1112} 1113 1114napi_value Worker::ParentPortRemoveEventListener(napi_env env, napi_callback_info cbinfo) 1115{ 1116 size_t argc = NapiHelper::GetCallbackInfoArgc(env, cbinfo); 1117 if (argc < 1) { 1118 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the number of parameters must be more than 2."); 1119 return nullptr; 1120 } 1121 1122 napi_value* args = new napi_value[argc]; 1123 ObjectScope<napi_value> scope(args, true); 1124 Worker* worker = nullptr; 1125 napi_get_cb_info(env, cbinfo, &argc, args, nullptr, reinterpret_cast<void**>(&worker)); 1126 1127 if (!NapiHelper::IsString(env, args[0])) { 1128 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "the type of worker listener 1st param must be string."); 1129 return nullptr; 1130 } 1131 1132 if (argc > 1 && !NapiHelper::IsCallable(env, args[1])) { 1133 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, 1134 "the type of worker listener second param must be callable."); 1135 return nullptr; 1136 } 1137 1138 if (worker == nullptr || !worker->IsNotTerminate()) { 1139 HILOG_ERROR("worker:: when post message to host occur worker is nullptr"); 1140 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "worker is not running."); 1141 return nullptr; 1142 } 1143 1144 napi_ref callback = nullptr; 1145 if (argc > 1 && NapiHelper::IsCallable(env, args[1])) { 1146 napi_create_reference(env, args[1], 1, &callback); 1147 } 1148 1149 char* typeStr = NapiHelper::GetChars(env, args[0]); 1150 if (typeStr == nullptr) { 1151 ErrorHelper::ThrowError(env, ErrorHelper::TYPE_ERROR, "worker listener type must be not null."); 1152 return nullptr; 1153 } 1154 worker->ParentPortRemoveListenerInner(env, typeStr, callback); 1155 CloseHelp::DeletePointer(typeStr, true); 1156 NapiHelper::DeleteReference(env, callback); 1157 return NapiHelper::GetUndefinedValue(env); 1158} 1159 1160napi_value Worker::ParentPortRemoveAllListener(napi_env env, napi_callback_info cbinfo) 1161{ 1162 Worker* worker = nullptr; 1163 napi_get_cb_info(env, cbinfo, nullptr, nullptr, nullptr, reinterpret_cast<void**>(&worker)); 1164 1165 if (worker == nullptr || !worker->IsNotTerminate()) { 1166 HILOG_ERROR("worker:: when post message to host occur worker is nullptr"); 1167 WorkerThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, 1168 "worker is nullptr when ParentPortRemoveAllListener"); 1169 return nullptr; 1170 } 1171 1172 worker->ParentPortRemoveAllListenerInner(); 1173 return NapiHelper::GetUndefinedValue(env); 1174} 1175 1176void Worker::GetContainerScopeId(napi_env env) 1177{ 1178 NativeEngine* hostEngine = reinterpret_cast<NativeEngine*>(env); 1179 scopeId_ = hostEngine->GetContainerScopeIdFunc(); 1180} 1181 1182void Worker::AddGlobalCallObject(const std::string &instanceName, napi_ref obj) 1183{ 1184 globalCallObjects_.insert_or_assign(instanceName, obj); 1185} 1186 1187bool Worker::RemoveGlobalCallObject(const std::string &instanceName) 1188{ 1189 for (auto iter = globalCallObjects_.begin(); iter != globalCallObjects_.end(); iter++) { 1190 if (iter->first == instanceName) { 1191 NapiHelper::DeleteReference(hostEnv_, iter->second); 1192 globalCallObjects_.erase(iter); 1193 return true; 1194 } 1195 } 1196 return false; 1197} 1198 1199void Worker::ClearGlobalCallObject() 1200{ 1201 for (auto iter = globalCallObjects_.begin(); iter != globalCallObjects_.end(); iter++) { 1202 napi_ref objRef = iter->second; 1203 NapiHelper::DeleteReference(hostEnv_, objRef); 1204 } 1205 globalCallObjects_.clear(); 1206} 1207 1208void Worker::StartExecuteInThread(napi_env env, const char* script) 1209{ 1210 HILOG_INFO("worker:: Start execute in the thread!"); 1211 // 1. init hostHandle in host loop 1212 uv_loop_t* loop = NapiHelper::GetLibUV(env); 1213 if (loop == nullptr) { 1214 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "engine loop is null"); 1215 CloseHelp::DeletePointer(script, true); 1216 return; 1217 } 1218 GetContainerScopeId(env); 1219#if defined(ENABLE_WORKER_EVENTHANDLER) 1220 if (!OHOS::AppExecFwk::EventRunner::IsAppMainThread()) { 1221 isMainThreadWorker_ = false; 1222 InitHostHandle(loop); 1223 } else if (isLimitedWorker_) { 1224 InitHostHandle(loop); 1225 } 1226#else 1227 InitHostHandle(loop); 1228#endif 1229 1230 // 2. copy the script 1231 script_ = std::string(script); 1232 // isBundle : FA mode and BundlePack. 1233 bool isBundle = reinterpret_cast<NativeEngine*>(env)->GetIsBundle(); 1234 // if worker file is packed in har, need find moduleName in hostVM, and concat new recordName. 1235 bool isHar = script_.find_first_of(PathHelper::NAME_SPACE_TAG) == 0; 1236 if ((isHar && script_.find(PathHelper::PREFIX_BUNDLE) == std::string::npos) || 1237 (!isBundle && script_.find_first_of(PathHelper::POINT_TAG) == 0)) { 1238 PathHelper::ConcatFileNameForWorker(env, script_, fileName_, isRelativePath_); 1239 HILOG_INFO("worker:: Concated worker recordName: %{public}s, fileName: %{public}s", 1240 script_.c_str(), fileName_.c_str()); 1241 } 1242 // check the path is vaild. 1243 if (!isBundle) { 1244 if (!PathHelper::CheckWorkerPath(env, script_, fileName_, isRelativePath_)) { 1245 EraseWorker(); 1246 HILOG_ERROR("worker:: the file path is invaild, can't find the file : %{public}s.", script); 1247 CloseHelp::DeletePointer(script, true); 1248 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_INVALID_FILEPATH, 1249 "the file path is invaild, can't find the file."); 1250 return; 1251 } 1252 } 1253 1254 // 3. create WorkerRunner to Execute 1255 if (!runner_) { 1256 runner_ = std::make_unique<WorkerRunner>(WorkerStartCallback(ExecuteInThread, this)); 1257 } 1258 if (runner_) { 1259 runner_->Execute(); // start a new thread 1260 } else { 1261 HILOG_ERROR("runner_ is nullptr"); 1262 } 1263 CloseHelp::DeletePointer(script, true); 1264} 1265 1266void Worker::ExecuteInThread(const void* data) 1267{ 1268 HITRACE_HELPER_START_TRACE(__PRETTY_FUNCTION__); 1269 auto worker = reinterpret_cast<Worker*>(const_cast<void*>(data)); 1270 // 1. create a runtime, nativeengine 1271 napi_env workerEnv = nullptr; 1272 { 1273 std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_); 1274 if (worker->HostIsStop() || worker->isHostEnvExited_) { 1275 HILOG_ERROR("worker:: host thread is stop"); 1276 CloseHelp::DeletePointer(worker, false); 1277 return; 1278 } 1279 napi_env env = worker->GetHostEnv(); 1280 if (worker->isLimitedWorker_) { 1281 napi_create_limit_runtime(env, &workerEnv); 1282 } else { 1283 napi_create_runtime(env, &workerEnv); 1284 } 1285 if (workerEnv == nullptr) { 1286 HILOG_ERROR("worker:: Worker create runtime error"); 1287 ErrorHelper::ThrowError(env, ErrorHelper::ERR_WORKER_NOT_RUNNING, "Worker create runtime error"); 1288 return; 1289 } 1290 if (worker->isLimitedWorker_) { 1291 reinterpret_cast<NativeEngine*>(workerEnv)->MarkRestrictedWorkerThread(); 1292 } else { 1293 // mark worker env is workerThread 1294 reinterpret_cast<NativeEngine*>(workerEnv)->MarkWorkerThread(); 1295 } 1296 // for load balance in taskpool 1297 reinterpret_cast<NativeEngine*>(env)->IncreaseSubEnvCounter(); 1298 1299 worker->SetWorkerEnv(workerEnv); 1300 } 1301 1302 uv_loop_t* loop = worker->GetWorkerLoop(); 1303 if (loop == nullptr) { 1304 HILOG_ERROR("worker:: Worker loop is nullptr"); 1305 return; 1306 } 1307 1308 // 2. add some preparation for the worker 1309 if (worker->PrepareForWorkerInstance()) { 1310 worker->workerOnMessageSignal_ = new uv_async_t; 1311 uv_async_init(loop, worker->workerOnMessageSignal_, reinterpret_cast<uv_async_cb>(Worker::WorkerOnMessage)); 1312 worker->workerOnMessageSignal_->data = worker; 1313#if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 1314 uv_async_init(loop, &worker->debuggerOnPostTaskSignal_, reinterpret_cast<uv_async_cb>( 1315 Worker::HandleDebuggerTask)); 1316#endif 1317 worker->UpdateWorkerState(RUNNING); 1318 // in order to invoke worker send before subThread start 1319 uv_async_send(worker->workerOnMessageSignal_); 1320 HITRACE_HELPER_FINISH_TRACE; 1321 // 3. start worker loop 1322 worker->Loop(); 1323 } else { 1324 HILOG_ERROR("worker:: worker PrepareForWorkerInstance fail"); 1325 worker->UpdateWorkerState(TERMINATED); 1326 HITRACE_HELPER_FINISH_TRACE; 1327 } 1328 worker->ReleaseWorkerThreadContent(); 1329 std::lock_guard<std::recursive_mutex> lock(worker->liveStatusLock_); 1330 if (worker->HostIsStop() || worker->isHostEnvExited_) { 1331 HILOG_INFO("worker:: host is stopped"); 1332 CloseHelp::DeletePointer(worker, false); 1333 } else { 1334 worker->PublishWorkerOverSignal(); 1335 } 1336} 1337 1338bool Worker::PrepareForWorkerInstance() 1339{ 1340 std::string rawFileName = script_; 1341 uint8_t* scriptContent = nullptr; 1342 size_t scriptContentSize = 0; 1343 std::vector<uint8_t> content; 1344 std::string workerAmi; 1345 { 1346 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_); 1347 if (HostIsStop() || isHostEnvExited_) { 1348 HILOG_INFO("worker:: host is stopped"); 1349 return false; 1350 } 1351 auto workerEngine = reinterpret_cast<NativeEngine*>(workerEnv_); 1352 auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_); 1353 // 1. init worker environment 1354#if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 1355 workerEngine->SetDebuggerPostTaskFunc([this](std::function<void()>&& task) { 1356 this->DebuggerOnPostTask(std::move(task)); 1357 }); 1358#endif 1359 if (!hostEngine->CallInitWorkerFunc(workerEngine)) { 1360 HILOG_ERROR("worker:: CallInitWorkerFunc error"); 1361 return false; 1362 } 1363 // 2. get uril content 1364 if (isRelativePath_) { 1365 rawFileName = fileName_; 1366 } 1367 if (!hostEngine->GetAbcBuffer(rawFileName.c_str(), &scriptContent, &scriptContentSize, content, workerAmi)) { 1368 HILOG_ERROR("worker:: GetAbcBuffer error"); 1369 return false; 1370 } 1371 } 1372 // add timer interface 1373 Timer::RegisterTime(workerEnv_); 1374 HILOG_DEBUG("worker:: stringContent size is %{public}zu", scriptContentSize); 1375 napi_value execScriptResult = nullptr; 1376 napi_status status = napi_run_actor(workerEnv_, scriptContent, scriptContentSize, 1377 workerAmi.c_str(), &execScriptResult, const_cast<char*>(script_.c_str())); 1378 if (status != napi_ok || execScriptResult == nullptr) { 1379 // An exception occurred when running the script. 1380 HILOG_ERROR("worker:: run script exception occurs, will handle exception"); 1381 HandleException(); 1382 return false; 1383 } 1384 1385 // 4. register worker name in DedicatedWorkerGlobalScope 1386 if (!name_.empty()) { 1387 napi_value nameValue = nullptr; 1388 napi_create_string_utf8(workerEnv_, name_.c_str(), name_.length(), &nameValue); 1389 NapiHelper::SetNamePropertyInGlobal(workerEnv_, "name", nameValue); 1390 } 1391 return true; 1392} 1393 1394void Worker::HostOnMessage(const uv_async_t* req) 1395{ 1396 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 1397 Worker* worker = static_cast<Worker*>(req->data); 1398 if (worker == nullptr) { 1399 HILOG_ERROR("worker:: worker is null when host onmessage."); 1400 return; 1401 } 1402 worker->HostOnMessageInner(); 1403} 1404 1405void Worker::HostOnMessageInner() 1406{ 1407 if (hostEnv_ == nullptr || HostIsStop()) { 1408 HILOG_ERROR("worker:: host thread maybe is over when host onmessage."); 1409 return; 1410 } 1411 1412 NativeEngine* engine = reinterpret_cast<NativeEngine*>(hostEnv_); 1413 if (!engine->InitContainerScopeFunc(scopeId_)) { 1414 HILOG_WARN("worker:: InitContainerScopeFunc error when HostOnMessageInner begin(only stage model)"); 1415 } 1416 1417 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_); 1418 napi_value callback = NapiHelper::GetNameProperty(hostEnv_, obj, "onmessage"); 1419 bool isCallable = NapiHelper::IsCallable(hostEnv_, callback); 1420 1421 MessageDataType data = nullptr; 1422 while (hostMessageQueue_.DeQueue(&data)) { 1423 // receive close signal. 1424 if (data == nullptr) { 1425 HILOG_DEBUG("worker:: worker received close signal"); 1426#if defined(ENABLE_WORKER_EVENTHANDLER) 1427 if ((!isMainThreadWorker_ || isLimitedWorker_) && !isHostEnvExited_) { 1428 CloseHostHandle(); 1429 } 1430#else 1431 if (!isHostEnvExited_) { 1432 CloseHostHandle(); 1433 } 1434#endif 1435 CloseHostCallback(); 1436 return; 1437 } 1438 // handle data, call worker onMessage function to handle. 1439 napi_status status = napi_ok; 1440 HandleScope scope(hostEnv_, status); 1441 NAPI_CALL_RETURN_VOID(hostEnv_, status); 1442 napi_value result = nullptr; 1443 status = napi_deserialize(hostEnv_, data, &result); 1444 napi_delete_serialization_data(hostEnv_, data); 1445 if (status != napi_ok || result == nullptr) { 1446 HostOnMessageErrorInner(); 1447 continue; 1448 } 1449 napi_value event = nullptr; 1450 napi_create_object(hostEnv_, &event); 1451 napi_set_named_property(hostEnv_, event, "data", result); 1452 napi_value argv[1] = { event }; 1453 if (isCallable) { 1454 napi_value callbackResult = nullptr; 1455 napi_call_function(hostEnv_, obj, callback, 1, argv, &callbackResult); 1456 } 1457 // handle listeners. 1458 HandleEventListeners(hostEnv_, obj, 1, argv, "message"); 1459 HandleHostException(); 1460 } 1461 if (!engine->FinishContainerScopeFunc(scopeId_)) { 1462 HILOG_WARN("worker:: FinishContainerScopeFunc error when HostOnMessageInner end(only stage model)"); 1463 } 1464} 1465 1466void Worker::HostOnGlobalCall(const uv_async_t* req) 1467{ 1468 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 1469 Worker* worker = static_cast<Worker*>(req->data); 1470 if (worker == nullptr) { 1471 HILOG_ERROR("worker:: worker is null"); 1472 return; 1473 } 1474 worker->HostOnGlobalCallInner(); 1475} 1476 1477void Worker::HostOnGlobalCallInner() 1478{ 1479 if (hostEnv_ == nullptr || HostIsStop()) { 1480 HILOG_ERROR("worker:: host thread maybe is over when host onmessage."); 1481 globalCallSuccess_ = false; 1482 cv_.notify_one(); 1483 return; 1484 } 1485 1486 NativeEngine* engine = reinterpret_cast<NativeEngine*>(hostEnv_); 1487 if (!engine->InitContainerScopeFunc(scopeId_)) { 1488 HILOG_WARN("worker:: InitContainerScopeFunc error when HostOnMessageInner begin(only stage model)"); 1489 } 1490 1491 if (hostGlobalCallQueue_.IsEmpty()) { 1492 HILOG_ERROR("worker:: message queue is empty when HostOnGlobalCallInner"); 1493 globalCallSuccess_ = false; 1494 cv_.notify_one(); 1495 return; 1496 } 1497 MessageDataType data = nullptr; 1498 uint32_t currentCallId = 0; 1499 size_t size = hostGlobalCallQueue_.GetSize(); 1500 if (size < 0 || size > GLOBAL_CALL_MAX_COUNT) { 1501 HILOG_ERROR("worker:: hostGlobalCallQueue_ size error"); 1502 globalCallSuccess_ = false; 1503 cv_.notify_one(); 1504 return; 1505 } 1506 for (size_t i = 0; i < size; i++) { 1507 std::pair<uint32_t, MessageDataType> pair = hostGlobalCallQueue_.Front(); 1508 hostGlobalCallQueue_.Pop(); 1509 if (pair.first == globalCallId_) { 1510 currentCallId = pair.first; 1511 data = pair.second; 1512 break; 1513 } 1514 napi_delete_serialization_data(hostEnv_, pair.second); 1515 } 1516 napi_value argsArray = nullptr; 1517 napi_status status = napi_ok; 1518 status = napi_deserialize(hostEnv_, data, &argsArray); 1519 napi_delete_serialization_data(hostEnv_, data); 1520 if (status != napi_ok || argsArray == nullptr) { 1521 AddGlobalCallError(ErrorHelper::ERR_WORKER_SERIALIZATION); 1522 globalCallSuccess_ = false; 1523 cv_.notify_one(); 1524 return; 1525 } 1526 napi_value instanceName = nullptr; 1527 napi_get_element(hostEnv_, argsArray, 0, &instanceName); 1528 napi_value methodName = nullptr; 1529 napi_get_element(hostEnv_, argsArray, 1, &methodName); 1530 1531 std::string instanceNameStr = NapiHelper::GetString(hostEnv_, instanceName); 1532 auto iter = globalCallObjects_.find(instanceNameStr); 1533 if (iter == globalCallObjects_.end()) { 1534 HILOG_ERROR("worker:: there is no instance: %{public}s registered for global call", instanceNameStr.c_str()); 1535 AddGlobalCallError(ErrorHelper::ERR_TRIGGER_NONEXIST_EVENT); 1536 globalCallSuccess_ = false; 1537 cv_.notify_one(); 1538 return; 1539 } 1540 napi_ref objRef = iter->second; 1541 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, objRef); 1542 bool hasProperty = false; 1543 napi_has_property(hostEnv_, obj, methodName, &hasProperty); 1544 if (!hasProperty) { 1545 std::string methodNameStr = NapiHelper::GetString(hostEnv_, methodName); 1546 HILOG_ERROR("worker:: registered obj for global call has no method: %{public}s", methodNameStr.c_str()); 1547 AddGlobalCallError(ErrorHelper::ERR_CALL_METHOD_ON_BINDING_OBJ); 1548 globalCallSuccess_ = false; 1549 cv_.notify_one(); 1550 return; 1551 } 1552 napi_value method = nullptr; 1553 napi_get_property(hostEnv_, obj, methodName, &method); 1554 // call method must not be generator function or async function 1555 bool validMethod = NapiHelper::IsCallable(hostEnv_, method) && !NapiHelper::IsAsyncFunction(hostEnv_, method) && 1556 !NapiHelper::IsGeneratorFunction(hostEnv_, method); 1557 if (!validMethod) { 1558 std::string methodNameStr = NapiHelper::GetString(hostEnv_, methodName); 1559 HILOG_ERROR("worker:: method %{public}s shall be callable and not async or generator method", 1560 methodNameStr.c_str()); 1561 AddGlobalCallError(ErrorHelper::ERR_CALL_METHOD_ON_BINDING_OBJ); 1562 globalCallSuccess_ = false; 1563 cv_.notify_one(); 1564 return; 1565 } 1566 uint32_t argc = 0; 1567 napi_get_array_length(hostEnv_, argsArray, &argc); 1568 napi_value* args = nullptr; 1569 ObjectScope<napi_value> scope(args, true); 1570 if (argc > BEGIN_INDEX_OF_ARGUMENTS) { 1571 args = new napi_value[argc - BEGIN_INDEX_OF_ARGUMENTS]; 1572 for (uint32_t index = 0; index < argc - BEGIN_INDEX_OF_ARGUMENTS; index++) { 1573 napi_get_element(hostEnv_, argsArray, index + BEGIN_INDEX_OF_ARGUMENTS, &args[index]); 1574 } 1575 } 1576 1577 napi_value res = nullptr; 1578 napi_call_function(hostEnv_, obj, method, argc - BEGIN_INDEX_OF_ARGUMENTS, args, &res); 1579 bool hasPendingException = NapiHelper::IsExceptionPending(hostEnv_); 1580 if (hasPendingException) { 1581 napi_value exception = nullptr; 1582 napi_get_and_clear_last_exception(hostEnv_, &exception); 1583 napi_throw(hostEnv_, exception); 1584 globalCallSuccess_ = false; 1585 cv_.notify_one(); 1586 return; 1587 } 1588 // defautly not transfer 1589 napi_value undefined = NapiHelper::GetUndefinedValue(hostEnv_); 1590 // meaningless to copy sendable object when call globalObject 1591 bool defaultClone = true; 1592 bool defaultTransfer = false; 1593 status = napi_serialize_inner(hostEnv_, res, undefined, undefined, defaultTransfer, defaultClone, &data); 1594 if (status != napi_ok || data == nullptr) { 1595 AddGlobalCallError(ErrorHelper::ERR_WORKER_SERIALIZATION); 1596 globalCallSuccess_ = false; 1597 cv_.notify_one(); 1598 return; 1599 } 1600 // drop and destruct result if timeout 1601 if (currentCallId != globalCallId_ || currentCallId == 0) { 1602 napi_delete_serialization_data(hostEnv_, data); 1603 cv_.notify_one(); 1604 return; 1605 } 1606 workerGlobalCallQueue_.EnQueue(data); 1607 globalCallSuccess_ = true; 1608 cv_.notify_one(); 1609} 1610 1611void Worker::AddGlobalCallError(int32_t errCode, napi_value errData) 1612{ 1613 globalCallErrors_.push({errCode, errData}); 1614} 1615 1616void Worker::HandleGlobalCallError(napi_env env) 1617{ 1618 while (!globalCallErrors_.empty()) { 1619 std::pair<int32_t, napi_value> pair = globalCallErrors_.front(); 1620 globalCallErrors_.pop(); 1621 int32_t errCode = pair.first; 1622 ErrorHelper::ThrowError(env, errCode); 1623 } 1624} 1625 1626void Worker::ClearGlobalCallError(napi_env env) 1627{ 1628 while (!globalCallErrors_.empty()) { 1629 std::pair<int32_t, napi_value> pair = globalCallErrors_.front(); 1630 globalCallErrors_.pop(); 1631 if (pair.second != nullptr) { 1632 napi_delete_serialization_data(env, pair.second); 1633 } 1634 } 1635} 1636 1637void Worker::CallHostFunction(size_t argc, const napi_value* argv, const char* methodName) const 1638{ 1639 if (hostEnv_ == nullptr) { 1640 HILOG_ERROR("worker:: host thread maybe is over"); 1641 return; 1642 } 1643 if (HostIsStop()) { 1644 HILOG_ERROR("worker:: host thread maybe is over"); 1645 WorkerThrowError(hostEnv_, ErrorHelper::ERR_WORKER_NOT_RUNNING, 1646 "host thread maybe is over when CallHostFunction"); 1647 return; 1648 } 1649 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_); 1650 napi_value callback = NapiHelper::GetNameProperty(hostEnv_, obj, methodName); 1651 bool isCallable = NapiHelper::IsCallable(hostEnv_, callback); 1652 if (!isCallable) { 1653 HILOG_DEBUG("worker:: host thread %{public}s is not Callable", methodName); 1654 return; 1655 } 1656 napi_value callbackResult = nullptr; 1657 napi_call_function(hostEnv_, obj, callback, argc, argv, &callbackResult); 1658 HandleHostException(); 1659} 1660 1661void Worker::CloseHostCallback() 1662{ 1663 { 1664 napi_status status = napi_ok; 1665 HandleScope scope(hostEnv_, status); 1666 NAPI_CALL_RETURN_VOID(hostEnv_, status); 1667 napi_value exitValue = nullptr; 1668 if (isErrorExit_) { 1669 napi_create_int32(hostEnv_, 1, &exitValue); // 1 : exit because of error 1670 } else { 1671 napi_create_int32(hostEnv_, 0, &exitValue); // 0 : exit normally 1672 } 1673 napi_value argv[1] = { exitValue }; 1674 CallHostFunction(1, argv, "onexit"); 1675 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_); 1676 // handle listeners 1677 HandleEventListeners(hostEnv_, obj, 1, argv, "exit"); 1678 } 1679 EraseWorker(); 1680 CloseHelp::DeletePointer(this, false); 1681} 1682 1683void Worker::HostOnError(const uv_async_t* req) 1684{ 1685 Worker* worker = static_cast<Worker*>(req->data); 1686 if (worker == nullptr) { 1687 HILOG_ERROR("worker:: worker is null"); 1688 return; 1689 } 1690 worker->HostOnErrorInner(); 1691 worker->TerminateInner(); 1692} 1693 1694void Worker::HostOnErrorInner() 1695{ 1696 if (hostEnv_ == nullptr || HostIsStop()) { 1697 HILOG_ERROR("worker:: host thread maybe is over when host onerror."); 1698 return; 1699 } 1700 napi_status status = napi_ok; 1701 HandleScope scope(hostEnv_, status); 1702 NAPI_CALL_RETURN_VOID(hostEnv_, status); 1703 NativeEngine* hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_); 1704 if (!hostEngine->InitContainerScopeFunc(scopeId_)) { 1705 HILOG_WARN("worker:: InitContainerScopeFunc error when onerror begin(only stage model)"); 1706 } 1707 1708 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_); 1709 napi_value callback = NapiHelper::GetNameProperty(hostEnv_, obj, "onerror"); 1710 bool isCallable = NapiHelper::IsCallable(hostEnv_, callback); 1711 1712 MessageDataType data; 1713 while (errorQueue_.DeQueue(&data)) { 1714 napi_value result = nullptr; 1715 napi_deserialize(hostEnv_, data, &result); 1716 napi_delete_serialization_data(hostEnv_, data); 1717 1718 napi_value argv[1] = { result }; 1719 if (isCallable) { 1720 napi_value callbackResult = nullptr; 1721 napi_call_function(hostEnv_, obj, callback, 1, argv, &callbackResult); 1722 } 1723 // handle listeners 1724 bool isHandle = HandleEventListeners(hostEnv_, obj, 1, argv, "error"); 1725 if (!isCallable && !isHandle) { 1726 napi_value businessError = ErrorHelper::ObjectToError(hostEnv_, result); 1727 napi_throw(hostEnv_, businessError); 1728 HandleHostException(); 1729 return; 1730 } 1731 HandleHostException(); 1732 } 1733 if (!hostEngine->FinishContainerScopeFunc(scopeId_)) { 1734 HILOG_WARN("worker:: FinishContainerScopeFunc error when onerror end(only stage model)"); 1735 } 1736} 1737 1738void Worker::PostMessageInner(MessageDataType data) 1739{ 1740 if (IsTerminated()) { 1741 HILOG_DEBUG("worker:: worker has been terminated when PostMessageInner."); 1742 return; 1743 } 1744 workerMessageQueue_.EnQueue(data); 1745 std::lock_guard<std::mutex> lock(workerOnmessageMutex_); 1746 if (workerOnMessageSignal_ != nullptr && !uv_is_closing((uv_handle_t*)workerOnMessageSignal_)) { 1747 uv_async_send(workerOnMessageSignal_); 1748 } 1749} 1750 1751void Worker::HostOnMessageErrorInner() 1752{ 1753 if (hostEnv_ == nullptr || HostIsStop()) { 1754 HILOG_ERROR("worker:: host thread maybe is over"); 1755 return; 1756 } 1757 napi_value obj = NapiHelper::GetReferenceValue(hostEnv_, workerRef_); 1758 CallHostFunction(0, nullptr, "onmessageerror"); 1759 // handle listeners 1760 HandleEventListeners(hostEnv_, obj, 0, nullptr, "messageerror"); 1761} 1762 1763void Worker::TerminateInner() 1764{ 1765 if (IsTerminated() || IsTerminating()) { 1766 HILOG_INFO("worker:: worker is not in running when TerminateInner"); 1767 return; 1768 } 1769 // 1. Update State 1770 UpdateWorkerState(TERMINATEING); 1771 // 2. send null signal 1772 PostMessageInner(nullptr); 1773} 1774 1775void Worker::CloseInner() 1776{ 1777 bool expected = false; 1778 if (isTerminated_.compare_exchange_weak(expected, true)) { 1779 HILOG_INFO("worker:: Close worker"); 1780 } else { 1781 HILOG_DEBUG("worker:: worker is terminated when Close"); 1782 return; 1783 } 1784 UpdateWorkerState(TERMINATEING); 1785 TerminateWorker(); 1786} 1787 1788bool Worker::UpdateWorkerState(RunnerState state) 1789{ 1790 bool done = false; 1791 do { 1792 RunnerState oldState = runnerState_.load(std::memory_order_acquire); 1793 if (oldState >= state) { 1794 // make sure state sequence is start, running, terminating, terminated 1795 return false; 1796 } 1797 done = runnerState_.compare_exchange_strong(oldState, state); 1798 } while (!done); 1799 return true; 1800} 1801 1802bool Worker::UpdateHostState(HostState state) 1803{ 1804 bool done = false; 1805 do { 1806 HostState oldState = hostState_.load(std::memory_order_acquire); 1807 if (oldState >= state) { 1808 // make sure state sequence is ACTIVE, INACTIVE 1809 return false; 1810 } 1811 done = hostState_.compare_exchange_strong(oldState, state); 1812 } while (!done); 1813 return true; 1814} 1815 1816void Worker::TerminateWorker() 1817{ 1818 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 1819 // when there is no active handle, worker loop will stop automatic. 1820 { 1821 std::lock_guard<std::mutex> lock(workerOnmessageMutex_); 1822 uv_close(reinterpret_cast<uv_handle_t*>(workerOnMessageSignal_), [](uv_handle_t* handle) { 1823 delete reinterpret_cast<uv_async_t*>(handle); 1824 handle = nullptr; 1825 }); 1826 } 1827#if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 1828 uv_close(reinterpret_cast<uv_handle_t*>(&debuggerOnPostTaskSignal_), nullptr); 1829#endif 1830 CloseWorkerCallback(); 1831 uv_loop_t* loop = GetWorkerLoop(); 1832 if (loop != nullptr) { 1833 Timer::ClearEnvironmentTimer(workerEnv_); 1834 uv_stop(loop); 1835 } 1836 UpdateWorkerState(TERMINATED); 1837} 1838 1839void Worker::PublishWorkerOverSignal() 1840{ 1841 if (HostIsStop()) { 1842 return; 1843 } 1844 // post nullptr tell host worker is not running 1845 hostMessageQueue_.EnQueue(nullptr); 1846#if defined(ENABLE_WORKER_EVENTHANDLER) 1847 if (isMainThreadWorker_ && !isLimitedWorker_) { 1848 PostWorkerOverTask(); 1849 } else { 1850 uv_async_send(hostOnMessageSignal_); 1851 } 1852#else 1853 uv_async_send(hostOnMessageSignal_); 1854#endif 1855} 1856 1857#if defined(ENABLE_WORKER_EVENTHANDLER) 1858void Worker::PostWorkerOverTask() 1859{ 1860 auto hostOnOverSignalTask = [this]() { 1861 if (IsValidWorker(this)) { 1862 HILOG_INFO("worker:: host thread receive terminate signal."); 1863 HITRACE_HELPER_METER_NAME("Worker:: HostOnTerminateSignal"); 1864 this->HostOnMessageInner(); 1865 } 1866 }; 1867 GetMainThreadHandler()->PostTask(hostOnOverSignalTask, "WorkerHostOnOverSignalTask", 1868 0, OHOS::AppExecFwk::EventQueue::Priority::HIGH); 1869} 1870 1871void Worker::PostWorkerErrorTask() 1872{ 1873 auto hostOnErrorTask = [this]() { 1874 if (IsValidWorker(this)) { 1875 HILOG_INFO("worker:: host thread receive error message."); 1876 HITRACE_HELPER_METER_NAME("Worker:: HostOnErrorMessage"); 1877 this->HostOnErrorInner(); 1878 this->TerminateInner(); 1879 } 1880 }; 1881 GetMainThreadHandler()->PostTask(hostOnErrorTask, "WorkerHostOnErrorTask", 1882 0, OHOS::AppExecFwk::EventQueue::Priority::HIGH); 1883} 1884 1885void Worker::PostWorkerMessageTask() 1886{ 1887 auto hostOnMessageTask = [this]() { 1888 if (IsValidWorker(this)) { 1889 HILOG_DEBUG("worker:: host thread receive message."); 1890 HITRACE_HELPER_METER_NAME("Worker:: HostOnMessage"); 1891 this->HostOnMessageInner(); 1892 } 1893 }; 1894 GetMainThreadHandler()->PostTask(hostOnMessageTask, "WorkerHostOnMessageTask", 1895 0, OHOS::AppExecFwk::EventQueue::Priority::HIGH); 1896} 1897 1898void Worker::PostWorkerGlobalCallTask() 1899{ 1900 auto hostOnGlobalCallTask = [this]() { 1901 if (IsValidWorker(this)) { 1902 HILOG_DEBUG("worker:: host thread receive globalCall signal."); 1903 HITRACE_HELPER_METER_NAME("Worker:: HostOnGlobalCallSignal"); 1904 this->HostOnGlobalCallInner(); 1905 } 1906 }; 1907 GetMainThreadHandler()->PostTask(hostOnGlobalCallTask, "WorkerHostOnGlobalCallTask", 1908 0, OHOS::AppExecFwk::EventQueue::Priority::HIGH); 1909} 1910#endif 1911 1912bool Worker::IsValidWorker(Worker* worker) 1913{ 1914 std::lock_guard<std::mutex> lock(g_workersMutex); 1915 std::list<Worker*>::iterator it = std::find(g_workers.begin(), g_workers.end(), worker); 1916 if (it == g_workers.end()) { 1917 return false; 1918 } 1919 return true; 1920} 1921 1922void Worker::WorkerOnMessage(const uv_async_t* req) 1923{ 1924 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 1925 Worker* worker = static_cast<Worker*>(req->data); 1926 if (worker == nullptr) { 1927 HILOG_ERROR("worker::worker is null"); 1928 return; 1929 } 1930 worker->WorkerOnMessageInner(); 1931} 1932 1933void Worker::WorkerOnMessageInner() 1934{ 1935 if (IsTerminated()) { 1936 return; 1937 } 1938 napi_status status; 1939 napi_handle_scope scope = nullptr; 1940 status = napi_open_handle_scope(workerEnv_, &scope); 1941 if (status != napi_ok || scope == nullptr) { 1942 HILOG_ERROR("worker:: WorkerOnMessage open handle scope failed."); 1943 return; 1944 } 1945 MessageDataType data = nullptr; 1946 while (!IsTerminated() && workerMessageQueue_.DeQueue(&data)) { 1947 if (data == nullptr) { 1948 HILOG_DEBUG("worker:: worker reveive terminate signal"); 1949 // Close handlescope need before TerminateWorker 1950 napi_close_handle_scope(workerEnv_, scope); 1951 TerminateWorker(); 1952 return; 1953 } 1954 napi_value result = nullptr; 1955 status = napi_deserialize(workerEnv_, data, &result); 1956 napi_delete_serialization_data(workerEnv_, data); 1957 if (status != napi_ok || result == nullptr) { 1958 WorkerOnMessageErrorInner(); 1959 continue; 1960 } 1961 1962 napi_value event = nullptr; 1963 napi_create_object(workerEnv_, &event); 1964 napi_set_named_property(workerEnv_, event, "data", result); 1965 napi_value argv[1] = { event }; 1966 CallWorkerFunction(1, argv, "onmessage", true); 1967 1968 napi_value obj = NapiHelper::GetReferenceValue(workerEnv_, this->workerPort_); 1969 ParentPortHandleEventListeners(workerEnv_, obj, 1, argv, "message", true); 1970 } 1971 napi_close_handle_scope(workerEnv_, scope); 1972} 1973 1974bool Worker::HandleEventListeners(napi_env env, napi_value recv, size_t argc, const napi_value* argv, const char* type) 1975{ 1976 std::string listener(type); 1977 auto iter = eventListeners_.find(listener); 1978 if (iter == eventListeners_.end()) { 1979 HILOG_DEBUG("worker:: there is no listener for type %{public}s in host thread", type); 1980 return false; 1981 } 1982 1983 std::list<WorkerListener*>& listeners = iter->second; 1984 std::list<WorkerListener*>::iterator it = listeners.begin(); 1985 while (it != listeners.end()) { 1986 WorkerListener* data = *it++; 1987 napi_value callbackObj = NapiHelper::GetReferenceValue(env, data->callback_); 1988 if (!NapiHelper::IsCallable(env, callbackObj)) { 1989 HILOG_WARN("worker:: host thread listener %{public}s is not callable", type); 1990 return false; 1991 } 1992 napi_value callbackResult = nullptr; 1993 napi_call_function(env, recv, callbackObj, argc, argv, &callbackResult); 1994 if (!data->NextIsAvailable()) { 1995 listeners.remove(data); 1996 CloseHelp::DeletePointer(data, false); 1997 } 1998 } 1999 return true; 2000} 2001 2002void Worker::HandleHostException() const 2003{ 2004 if (!NapiHelper::IsExceptionPending(hostEnv_)) { 2005 return; 2006 } 2007 auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_); 2008 hostEngine->HandleUncaughtException(); 2009} 2010 2011void Worker::HandleException() 2012{ 2013 if (!NapiHelper::IsExceptionPending(workerEnv_)) { 2014 return; 2015 } 2016 2017 napi_status status = napi_ok; 2018 HandleScope scope(workerEnv_, status); 2019 NAPI_CALL_RETURN_VOID(workerEnv_, status); 2020 napi_value exception; 2021 napi_get_and_clear_last_exception(workerEnv_, &exception); 2022 if (exception == nullptr) { 2023 return; 2024 } 2025 2026 HandleUncaughtException(exception); 2027} 2028 2029void Worker::HandleUncaughtException(napi_value exception) 2030{ 2031 napi_value obj = ErrorHelper::TranslateErrorEvent(workerEnv_, exception); 2032 2033 // WorkerGlobalScope onerror 2034 WorkerOnErrorInner(obj); 2035 2036 if (hostEnv_ == nullptr) { 2037 HILOG_ERROR("worker:: host engine is nullptr."); 2038 return; 2039 } 2040 MessageDataType data = nullptr; 2041 napi_value undefined = NapiHelper::GetUndefinedValue(workerEnv_); 2042 napi_serialize_inner(workerEnv_, obj, undefined, undefined, false, true, &data); 2043 { 2044 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_); 2045 if (HostIsStop() || isHostEnvExited_) { 2046 return; 2047 } 2048 errorQueue_.EnQueue(data); 2049#if defined(ENABLE_WORKER_EVENTHANDLER) 2050 if (isMainThreadWorker_ && !isLimitedWorker_) { 2051 PostWorkerErrorTask(); 2052 } else { 2053 uv_async_send(hostOnErrorSignal_); 2054 } 2055#else 2056 uv_async_send(hostOnErrorSignal_); 2057#endif 2058 } 2059} 2060 2061void Worker::WorkerOnMessageErrorInner() 2062{ 2063 isErrorExit_ = true; 2064 CallWorkerFunction(0, nullptr, "onmessageerror", true); 2065 napi_value obj = NapiHelper::GetReferenceValue(workerEnv_, this->workerPort_); 2066 ParentPortHandleEventListeners(workerEnv_, obj, 0, nullptr, "messageerror", true); 2067} 2068 2069void Worker::PostMessageToHostInner(MessageDataType data) 2070{ 2071 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_); 2072 if (hostEnv_ != nullptr && !HostIsStop() && !isHostEnvExited_) { 2073 hostMessageQueue_.EnQueue(data); 2074#if defined(ENABLE_WORKER_EVENTHANDLER) 2075 if (isMainThreadWorker_ && !isLimitedWorker_) { 2076 PostWorkerMessageTask(); 2077 } else { 2078 uv_async_send(hostOnMessageSignal_); 2079 } 2080#else 2081 uv_async_send(hostOnMessageSignal_); 2082#endif 2083 } else { 2084 HILOG_ERROR("worker:: worker host engine is nullptr when PostMessageToHostInner."); 2085 } 2086} 2087 2088bool Worker::WorkerListener::operator==(const WorkerListener& listener) const 2089{ 2090 napi_value obj = NapiHelper::GetReferenceValue(listener.env_, listener.callback_); 2091 napi_value compareObj = NapiHelper::GetReferenceValue(env_, callback_); 2092 // the env of listener and cmp listener must be same env because of Synchronization method 2093 return NapiHelper::StrictEqual(env_, compareObj, obj); 2094} 2095 2096void Worker::AddListenerInner(napi_env env, const char* type, const WorkerListener* listener) 2097{ 2098 std::string typestr(type); 2099 auto iter = eventListeners_.find(typestr); 2100 if (iter == eventListeners_.end()) { 2101 std::list<WorkerListener*> listeners; 2102 listeners.emplace_back(const_cast<WorkerListener*>(listener)); 2103 eventListeners_[typestr] = listeners; 2104 } else { 2105 std::list<WorkerListener*>& listenerList = iter->second; 2106 std::list<WorkerListener*>::iterator it = std::find_if( 2107 listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, listener->callback_)); 2108 if (it != listenerList.end()) { 2109 return; 2110 } 2111 listenerList.emplace_back(const_cast<WorkerListener*>(listener)); 2112 } 2113} 2114 2115void Worker::RemoveListenerInner(napi_env env, const char* type, napi_ref callback) 2116{ 2117 std::string typestr(type); 2118 auto iter = eventListeners_.find(typestr); 2119 if (iter == eventListeners_.end()) { 2120 return; 2121 } 2122 std::list<WorkerListener*>& listenerList = iter->second; 2123 if (callback != nullptr) { 2124 std::list<WorkerListener*>::iterator it = 2125 std::find_if(listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, callback)); 2126 if (it != listenerList.end()) { 2127 CloseHelp::DeletePointer(*it, false); 2128 listenerList.erase(it); 2129 } 2130 } else { 2131 for (auto it = listenerList.begin(); it != listenerList.end(); it++) { 2132 CloseHelp::DeletePointer(*it, false); 2133 } 2134 eventListeners_.erase(typestr); 2135 } 2136} 2137 2138Worker::~Worker() 2139{ 2140 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_); 2141 if (!HostIsStop() && !isHostEnvExited_) { 2142 ReleaseHostThreadContent(); 2143 RemoveAllListenerInner(); 2144 ClearGlobalCallObject(); 2145 } 2146} 2147 2148void Worker::RemoveAllListenerInner() 2149{ 2150 for (auto iter = eventListeners_.begin(); iter != eventListeners_.end(); iter++) { 2151 std::list<WorkerListener*>& listeners = iter->second; 2152 for (auto item = listeners.begin(); item != listeners.end(); item++) { 2153 WorkerListener* listener = *item; 2154 CloseHelp::DeletePointer(listener, false); 2155 } 2156 } 2157 eventListeners_.clear(); 2158} 2159 2160void Worker::ReleaseHostThreadContent() 2161{ 2162 ClearHostMessage(hostEnv_); 2163 if (!HostIsStop()) { 2164 napi_status status = napi_ok; 2165 HandleScope scope(hostEnv_, status); 2166 NAPI_CALL_RETURN_VOID(hostEnv_, status); 2167 // 3. set thisVar's nativepointer be null 2168 napi_value thisVar = NapiHelper::GetReferenceValue(hostEnv_, workerRef_); 2169 Worker* worker = nullptr; 2170 napi_remove_wrap(hostEnv_, thisVar, reinterpret_cast<void**>(&worker)); 2171 hostEnv_ = nullptr; 2172 // 4. set workerRef_ be null 2173 workerRef_ = nullptr; 2174 } 2175} 2176 2177void Worker::WorkerOnErrorInner(napi_value error) 2178{ 2179 isErrorExit_ = true; 2180 napi_value argv[1] = { error }; 2181 CallWorkerFunction(1, argv, "onerror", false); 2182 napi_value obj = NapiHelper::GetReferenceValue(workerEnv_, this->workerPort_); 2183 ParentPortHandleEventListeners(workerEnv_, obj, 1, argv, "error", false); 2184} 2185 2186bool Worker::CallWorkerFunction(size_t argc, const napi_value* argv, const char* methodName, bool tryCatch) 2187{ 2188 if (workerEnv_ == nullptr) { 2189 HILOG_ERROR("Worker:: worker is not running when call workerPort.%{public}s.", methodName); 2190 return false; 2191 } 2192 napi_value callback = NapiHelper::GetNamePropertyInParentPort(workerEnv_, workerPort_, methodName); 2193 bool isCallable = NapiHelper::IsCallable(workerEnv_, callback); 2194 if (!isCallable) { 2195 HILOG_WARN("worker:: workerPort.%{public}s is not Callable", methodName); 2196 return false; 2197 } 2198 napi_value workerPortObj = NapiHelper::GetReferenceValue(workerEnv_, workerPort_); 2199 napi_value callbackResult = nullptr; 2200 napi_call_function(workerEnv_, workerPortObj, callback, argc, argv, &callbackResult); 2201 if (tryCatch && callbackResult == nullptr) { 2202 HILOG_ERROR("worker:: workerPort.%{public}s handle exception", methodName); 2203 HandleException(); 2204 return false; 2205 } 2206 return true; 2207} 2208 2209void Worker::CloseWorkerCallback() 2210{ 2211 CallWorkerFunction(0, nullptr, "onclose", true); 2212 // off worker inited environment 2213 { 2214 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_); 2215 if (HostIsStop() || isHostEnvExited_) { 2216 return; 2217 } 2218 auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_); 2219 if (!hostEngine->CallOffWorkerFunc(reinterpret_cast<NativeEngine*>(workerEnv_))) { 2220 HILOG_ERROR("worker:: CallOffWorkerFunc error"); 2221 } 2222 } 2223} 2224 2225void Worker::ReleaseWorkerThreadContent() 2226{ 2227 HITRACE_HELPER_METER_NAME(__PRETTY_FUNCTION__); 2228 { 2229 std::lock_guard<std::recursive_mutex> lock(liveStatusLock_); 2230 if (!HostIsStop() && !isHostEnvExited_) { 2231 auto hostEngine = reinterpret_cast<NativeEngine*>(hostEnv_); 2232 auto workerEngine = reinterpret_cast<NativeEngine*>(workerEnv_); 2233 if (hostEngine != nullptr && workerEngine != nullptr) { 2234 if (!hostEngine->DeleteWorker(workerEngine)) { 2235 HILOG_ERROR("worker:: DeleteWorker error"); 2236 } 2237 hostEngine->DecreaseSubEnvCounter(); 2238 } 2239 } 2240 if (isHostEnvExited_) { 2241 EraseWorker(); 2242 } 2243 } 2244 // 1. delete worker listener 2245 ParentPortRemoveAllListenerInner(); 2246 2247 // 2. delete worker's parentPort 2248 NapiHelper::DeleteReference(workerEnv_, workerPort_); 2249 workerPort_ = nullptr; 2250 2251 // 3. clear message send to worker thread 2252 workerMessageQueue_.Clear(workerEnv_); 2253 workerGlobalCallQueue_.Clear(workerEnv_); 2254 CloseHelp::DeletePointer(reinterpret_cast<NativeEngine*>(workerEnv_), false); 2255 workerEnv_ = nullptr; 2256} 2257 2258void Worker::ParentPortAddListenerInner(napi_env env, const char* type, const WorkerListener* listener) 2259{ 2260 std::string typestr(type); 2261 auto iter = parentPortEventListeners_.find(typestr); 2262 if (iter == parentPortEventListeners_.end()) { 2263 std::list<WorkerListener*> listeners; 2264 listeners.emplace_back(const_cast<WorkerListener*>(listener)); 2265 parentPortEventListeners_[typestr] = listeners; 2266 } else { 2267 std::list<WorkerListener*>& listenerList = iter->second; 2268 std::list<WorkerListener*>::iterator it = std::find_if( 2269 listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, listener->callback_)); 2270 if (it != listenerList.end()) { 2271 return; 2272 } 2273 listenerList.emplace_back(const_cast<WorkerListener*>(listener)); 2274 } 2275} 2276 2277void Worker::ParentPortRemoveAllListenerInner() 2278{ 2279 for (auto iter = parentPortEventListeners_.begin(); iter != parentPortEventListeners_.end(); iter++) { 2280 std::list<WorkerListener*>& listeners = iter->second; 2281 for (auto item = listeners.begin(); item != listeners.end(); item++) { 2282 WorkerListener* listener = *item; 2283 CloseHelp::DeletePointer(listener, false); 2284 } 2285 } 2286 parentPortEventListeners_.clear(); 2287} 2288 2289void Worker::ParentPortRemoveListenerInner(napi_env env, const char* type, napi_ref callback) 2290{ 2291 std::string typestr(type); 2292 auto iter = parentPortEventListeners_.find(typestr); 2293 if (iter == parentPortEventListeners_.end()) { 2294 return; 2295 } 2296 std::list<WorkerListener*>& listenerList = iter->second; 2297 if (callback != nullptr) { 2298 std::list<WorkerListener*>::iterator it = 2299 std::find_if(listenerList.begin(), listenerList.end(), Worker::FindWorkerListener(env, callback)); 2300 if (it != listenerList.end()) { 2301 CloseHelp::DeletePointer(*it, false); 2302 listenerList.erase(it); 2303 } 2304 } else { 2305 for (auto it = listenerList.begin(); it != listenerList.end(); it++) { 2306 CloseHelp::DeletePointer(*it, false); 2307 } 2308 parentPortEventListeners_.erase(typestr); 2309 } 2310} 2311 2312void Worker::ParentPortHandleEventListeners(napi_env env, napi_value recv, size_t argc, 2313 const napi_value* argv, const char* type, bool tryCatch) 2314{ 2315 std::string listener(type); 2316 auto iter = parentPortEventListeners_.find(listener); 2317 if (iter == parentPortEventListeners_.end()) { 2318 HILOG_DEBUG("worker:: there is no listener for type %{public}s in worker thread", type); 2319 return; 2320 } 2321 2322 std::list<WorkerListener*>& listeners = iter->second; 2323 std::list<WorkerListener*>::iterator it = listeners.begin(); 2324 while (it != listeners.end()) { 2325 WorkerListener* data = *it++; 2326 napi_value callbackObj = NapiHelper::GetReferenceValue(env, data->callback_); 2327 if (!NapiHelper::IsCallable(env, callbackObj)) { 2328 HILOG_WARN("worker:: workerPort.addEventListener %{public}s is not callable", type); 2329 return; 2330 } 2331 napi_value callbackResult = nullptr; 2332 napi_call_function(env, recv, callbackObj, argc, argv, &callbackResult); 2333 if (!data->NextIsAvailable()) { 2334 listeners.remove(data); 2335 CloseHelp::DeletePointer(data, false); 2336 } 2337 if (tryCatch && callbackResult == nullptr) { 2338 HandleException(); 2339 return; 2340 } 2341 } 2342} 2343 2344void Worker::WorkerThrowError(napi_env env, int32_t errCode, const char* errMessage) 2345{ 2346 auto mainThreadEngine = NativeEngine::GetMainThreadEngine(); 2347 if (mainThreadEngine == nullptr) { 2348 HILOG_ERROR("worker:: mainThreadEngine is nullptr"); 2349 return; 2350 } 2351 if (mainThreadEngine->IsTargetWorkerVersion(WorkerVersion::NEW)) { 2352 ErrorHelper::ThrowError(env, errCode, errMessage); 2353 } 2354} 2355 2356bool Worker::CanCreateWorker(napi_env env, WorkerVersion target) 2357{ 2358 auto mainThreadEngine = NativeEngine::GetMainThreadEngine(); 2359 if (mainThreadEngine == nullptr) { 2360 HILOG_ERROR("worker:: mainThreadEngine is nullptr"); 2361 return false; 2362 } 2363 if (mainThreadEngine->CheckAndSetWorkerVersion(WorkerVersion::NONE, target) || 2364 mainThreadEngine->IsTargetWorkerVersion(target)) { 2365 return true; 2366 } 2367 return false; 2368} 2369 2370#if !defined(WINDOWS_PLATFORM) && !defined(MAC_PLATFORM) 2371void Worker::HandleDebuggerTask(const uv_async_t* req) 2372{ 2373 Worker* worker = DereferenceHelp::DereferenceOf(&Worker::debuggerOnPostTaskSignal_, req); 2374 if (worker == nullptr) { 2375 HILOG_ERROR("worker::worker is null"); 2376 return; 2377 } 2378 2379 worker->debuggerMutex_.lock(); 2380 auto task = std::move(worker->debuggerQueue_.front()); 2381 worker->debuggerQueue_.pop(); 2382 worker->debuggerMutex_.unlock(); 2383 task(); 2384} 2385 2386void Worker::DebuggerOnPostTask(std::function<void()>&& task) 2387{ 2388 if (IsTerminated()) { 2389 HILOG_ERROR("worker:: worker has been terminated."); 2390 return; 2391 } 2392 if (!uv_is_closing((uv_handle_t*)&debuggerOnPostTaskSignal_)) { 2393 std::lock_guard<std::mutex> lock(debuggerMutex_); 2394 debuggerQueue_.push(std::move(task)); 2395 uv_async_send(&debuggerOnPostTaskSignal_); 2396 } 2397} 2398#endif 2399 2400void Worker::InitHostHandle(uv_loop_t* loop) 2401{ 2402 hostOnMessageSignal_ = new uv_async_t; 2403 uv_async_init(loop, hostOnMessageSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnMessage)); 2404 hostOnMessageSignal_->data = this; 2405 hostOnErrorSignal_ = new uv_async_t; 2406 uv_async_init(loop, hostOnErrorSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnError)); 2407 hostOnErrorSignal_->data = this; 2408 hostOnGlobalCallSignal_ = new uv_async_t; 2409 uv_async_init(loop, hostOnGlobalCallSignal_, reinterpret_cast<uv_async_cb>(Worker::HostOnGlobalCall)); 2410 hostOnGlobalCallSignal_->data = this; 2411} 2412 2413void Worker::CloseHostHandle() 2414{ 2415 if (hostOnMessageSignal_ != nullptr && !uv_is_closing(reinterpret_cast<uv_handle_t*>(hostOnMessageSignal_))) { 2416 uv_close(reinterpret_cast<uv_handle_t*>(hostOnMessageSignal_), [](uv_handle_t* handle) { 2417 delete reinterpret_cast<uv_async_t*>(handle); 2418 handle = nullptr; 2419 }); 2420 } 2421 if (hostOnErrorSignal_ != nullptr && !uv_is_closing(reinterpret_cast<uv_handle_t*>(hostOnErrorSignal_))) { 2422 uv_close(reinterpret_cast<uv_handle_t*>(hostOnErrorSignal_), [](uv_handle_t* handle) { 2423 delete reinterpret_cast<uv_async_t*>(handle); 2424 handle = nullptr; 2425 }); 2426 } 2427 if (hostOnGlobalCallSignal_ != nullptr && !uv_is_closing(reinterpret_cast<uv_handle_t*>(hostOnGlobalCallSignal_))) { 2428 uv_close(reinterpret_cast<uv_handle_t*>(hostOnGlobalCallSignal_), [](uv_handle_t* handle) { 2429 delete reinterpret_cast<uv_async_t*>(handle); 2430 handle = nullptr; 2431 }); 2432 } 2433} 2434 2435void Worker::EraseWorker() 2436{ 2437 if (!isLimitedWorker_) { 2438 std::lock_guard<std::mutex> lock(g_workersMutex); 2439 std::list<Worker*>::iterator it = std::find(g_workers.begin(), g_workers.end(), this); 2440 if (it != g_workers.end()) { 2441 g_workers.erase(it); 2442 } 2443 } else { 2444 std::lock_guard<std::mutex> lock(g_limitedworkersMutex); 2445 std::list<Worker*>::iterator it = std::find(g_limitedworkers.begin(), g_limitedworkers.end(), this); 2446 if (it != g_limitedworkers.end()) { 2447 g_limitedworkers.erase(it); 2448 } 2449 } 2450} 2451 2452void Worker::ClearHostMessage(napi_env env) 2453{ 2454 hostMessageQueue_.Clear(env); 2455 hostGlobalCallQueue_.Clear(env); 2456 errorQueue_.Clear(env); 2457} 2458} // namespace Commonlibrary::Concurrent::WorkerModule 2459