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