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