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