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