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