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