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