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