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